refactor: sync code from uoj.ac (#14)
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Baoshuo Ren 2022-11-06 10:41:00 +08:00
commit 61aebec03e
Signed by: baoshuo
GPG Key ID: 00CB9680AB29F51A
146 changed files with 17676 additions and 11497 deletions

View File

@ -1,16 +0,0 @@
name: Lint & Test
on:
- push
- pull_request
jobs:
php-cs-fixer:
name: PHP-CS-Fixer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: PHP-CS-Fixer
uses: docker://oskarstark/php-cs-fixer-ga
with:
args: --config=./web/.php-cs-fixer.php --diff --dry-run

16
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"intelephense.format.braces": "k&r",
"intelephense.completion.triggerParameterHints": false
}

View File

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

View File

@ -1,2 +1,2 @@
USE `app_uoj233`; USE `app_uoj233`;
insert into judger_info (judger_name, password, ip) values ('compose_judger', '_judger_password_', 'uoj-judger'); insert into judger_info (judger_name, password, ip, display_name, description) values ('compose_judger', '_judger_password_', 'uoj-judger', '内建 Judger', '');

View File

@ -30,18 +30,18 @@ USE `app_uoj233`;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `best_ac_submissions` ( CREATE TABLE `best_ac_submissions` (
`problem_id` int(11) NOT NULL, `problem_id` int NOT NULL,
`submitter` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, `submitter` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`submission_id` int(11) NOT NULL, `submission_id` int NOT NULL,
`used_time` int(11) NOT NULL, `used_time` int NOT NULL,
`used_memory` int(11) NOT NULL, `used_memory` int NOT NULL,
`tot_size` int(11) NOT NULL, `tot_size` int NOT NULL,
`shortest_id` int(11) NOT NULL, `shortest_id` int NOT NULL,
`shortest_used_time` int(11) NOT NULL, `shortest_used_time` int NOT NULL,
`shortest_used_memory` int(11) NOT NULL, `shortest_used_memory` int NOT NULL,
`shortest_tot_size` int(11) NOT NULL, `shortest_tot_size` int NOT NULL,
PRIMARY KEY (`problem_id`,`submitter`) PRIMARY KEY (`problem_id`,`submitter`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci TABLESPACE `innodb_system`;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
-- --
@ -60,16 +60,20 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `blogs` ( CREATE TABLE `blogs` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int NOT NULL AUTO_INCREMENT,
`title` text COLLATE utf8mb4_unicode_ci NOT NULL, `title` text COLLATE utf8mb4_unicode_ci NOT NULL,
`content` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`post_time` datetime NOT NULL, `post_time` datetime NOT NULL,
`active_time` datetime NOT NULL,
`poster` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, `poster` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`content_md` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, `content_md` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`zan` int(11) NOT NULL, `zan` int NOT NULL DEFAULT '0',
`is_hidden` tinyint(1) NOT NULL, `is_hidden` tinyint(1) NOT NULL,
`type` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'B', `type` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'B',
PRIMARY KEY (`id`) PRIMARY KEY (`id`),
KEY `post_time` (`post_time`),
KEY `active_time` (`active_time`),
KEY `poster` (`poster`,`is_hidden`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -89,14 +93,19 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `blogs_comments` ( CREATE TABLE `blogs_comments` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int NOT NULL AUTO_INCREMENT,
`blog_id` int(11) NOT NULL, `blog_id` int NOT NULL,
`content` text COLLATE utf8mb4_unicode_ci NOT NULL, `content` text COLLATE utf8mb4_unicode_ci NOT NULL,
`post_time` datetime NOT NULL, `post_time` datetime NOT NULL,
`poster` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, `poster` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`zan` int(11) NOT NULL, `zan` int NOT NULL,
`reply_id` int(11) NOT NULL, `reply_id` int NOT NULL,
PRIMARY KEY (`id`) `is_hidden` tinyint(1) NOT NULL DEFAULT '0',
`reason_to_hide` varchar(10000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `reply_id` (`reply_id`,`id`),
KEY `blog_id` (`blog_id`,`post_time`),
KEY `blog_id_2` (`blog_id`,`reply_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -116,8 +125,8 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `blogs_tags` ( CREATE TABLE `blogs_tags` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int NOT NULL AUTO_INCREMENT,
`blog_id` int(11) NOT NULL, `blog_id` int NOT NULL,
`tag` varchar(30) NOT NULL, `tag` varchar(30) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `blog_id` (`blog_id`), KEY `blog_id` (`blog_id`),
@ -143,8 +152,8 @@ UNLOCK TABLES;
CREATE TABLE `click_zans` ( CREATE TABLE `click_zans` (
`type` char(2) COLLATE utf8mb4_unicode_ci NOT NULL, `type` char(2) COLLATE utf8mb4_unicode_ci NOT NULL,
`username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, `username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`target_id` int(11) NOT NULL, `target_id` int NOT NULL,
`val` tinyint(4) NOT NULL DEFAULT '1', `val` tinyint NOT NULL DEFAULT '1',
PRIMARY KEY (`type`,`target_id`,`username`) PRIMARY KEY (`type`,`target_id`,`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -165,15 +174,17 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `contests` ( CREATE TABLE `contests` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL, `name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`start_time` datetime NOT NULL, `start_time` datetime NOT NULL,
`last_min` int(11) NOT NULL, `end_time` datetime GENERATED ALWAYS AS ((`start_time` + interval `last_min` minute)) VIRTUAL NOT NULL,
`player_num` int(11) NOT NULL, `last_min` int NOT NULL,
`status` varchar(50) NOT NULL, `player_num` int NOT NULL DEFAULT '0',
`extra_config` varchar(200) NOT NULL, `status` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`zan` int(11) NOT NULL, `extra_config` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '{}',
PRIMARY KEY (`id`) `zan` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `status` (`status`,`id`) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -193,15 +204,17 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `contests_asks` ( CREATE TABLE `contests_asks` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int NOT NULL AUTO_INCREMENT,
`contest_id` int(11) NOT NULL, `contest_id` int NOT NULL,
`username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, `username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`question` text NOT NULL, `question` text NOT NULL,
`answer` text NOT NULL, `answer` text NOT NULL,
`post_time` datetime NOT NULL, `post_time` datetime NOT NULL,
`reply_time` datetime NOT NULL, `reply_time` datetime NOT NULL,
`is_hidden` tinyint(1) DEFAULT '0', `is_hidden` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`) PRIMARY KEY (`id`),
KEY `contest_id` (`contest_id`,`is_hidden`,`username`) USING BTREE,
KEY `username` (`username`,`contest_id`) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -221,7 +234,7 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `contests_notice` ( CREATE TABLE `contests_notice` (
`contest_id` int(11) NOT NULL, `contest_id` int NOT NULL,
`title` varchar(30) NOT NULL, `title` varchar(30) NOT NULL,
`content` varchar(500) NOT NULL, `content` varchar(500) NOT NULL,
`time` datetime NOT NULL, `time` datetime NOT NULL,
@ -246,7 +259,7 @@ UNLOCK TABLES;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `contests_permissions` ( CREATE TABLE `contests_permissions` (
`username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, `username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`contest_id` int(11) NOT NULL, `contest_id` int NOT NULL,
PRIMARY KEY (`username`,`contest_id`) PRIMARY KEY (`username`,`contest_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -267,10 +280,11 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `contests_problems` ( CREATE TABLE `contests_problems` (
`problem_id` int(11) NOT NULL, `problem_id` int NOT NULL,
`contest_id` int(11) NOT NULL, `contest_id` int NOT NULL,
`dfn` int(11) NOT NULL DEFAULT 0, `level` int NOT NULL DEFAULT 0,
PRIMARY KEY (`problem_id`,`contest_id`) PRIMARY KEY (`problem_id`,`contest_id`),
KEY `contest_id` (`contest_id`,`problem_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -291,9 +305,9 @@ UNLOCK TABLES;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `contests_registrants` ( CREATE TABLE `contests_registrants` (
`username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, `username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`contest_id` int(11) NOT NULL, `contest_id` int NOT NULL,
`has_participated` tinyint(1) NOT NULL, `has_participated` tinyint(1) NOT NULL,
`rank` int(11) NOT NULL, `final_rank` int NOT NULL,
PRIMARY KEY (`contest_id`,`username`) PRIMARY KEY (`contest_id`,`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -314,12 +328,14 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `contests_submissions` ( CREATE TABLE `contests_submissions` (
`contest_id` int(11) NOT NULL, `contest_id` int NOT NULL,
`submitter` varchar(20) NOT NULL, `submitter` varchar(20) NOT NULL,
`problem_id` int(11) NOT NULL, `problem_id` int NOT NULL,
`submission_id` int(11) NOT NULL, `submission_id` int NOT NULL,
`score` int(11) NOT NULL, `score` int NOT NULL,
`penalty` int(11) NOT NULL, `penalty` int NOT NULL,
`cnt` int DEFAULT NULL,
`n_failures` int DEFAULT NULL,
PRIMARY KEY (`contest_id`,`submitter`,`problem_id`) PRIMARY KEY (`contest_id`,`submitter`,`problem_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -340,11 +356,13 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `contests_reviews` ( CREATE TABLE `contests_reviews` (
`contest_id` int(11) NOT NULL, `contest_id` int NOT NULL,
`problem_id` int(11) NOT NULL DEFAULT 0, `problem_id` int NOT NULL DEFAULT 0,
`poster` varchar(20) NOT NULL, `poster` varchar(20) NOT NULL,
`content` text NOT NULL, `content` text NOT NULL,
PRIMARY KEY (`contest_id`,`problem_id`,`poster`) PRIMARY KEY (`contest_id`,`problem_id`,`poster`),
KEY `contest_id` (`contest_id`,`problem_id`),
KEY `poster` (`poster`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -364,7 +382,7 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `countdowns` ( CREATE TABLE `countdowns` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`title` text NOT NULL, `title` text NOT NULL,
`end_time` datetime NOT NULL, `end_time` datetime NOT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
@ -387,8 +405,8 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `custom_test_submissions` ( CREATE TABLE `custom_test_submissions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`problem_id` int(10) unsigned NOT NULL, `problem_id` int UNSIGNED NOT NULL,
`submit_time` datetime NOT NULL, `submit_time` datetime NOT NULL,
`submitter` varchar(20) NOT NULL, `submitter` varchar(20) NOT NULL,
`content` text NOT NULL, `content` text NOT NULL,
@ -396,8 +414,10 @@ CREATE TABLE `custom_test_submissions` (
`result` blob NOT NULL, `result` blob NOT NULL,
`status` varchar(20) NOT NULL, `status` varchar(20) NOT NULL,
`status_details` varchar(100) NOT NULL, `status_details` varchar(100) NOT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; KEY `submitter` (`submitter`,`problem_id`,`id`),
KEY `judge_time` (`judge_time`,`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
-- --
@ -416,10 +436,10 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `friend_links` ( CREATE TABLE `friend_links` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(40) NOT NULL, `title` varchar(40) NOT NULL,
`url` varchar(100) NOT NULL, `url` varchar(100) NOT NULL,
`level` int(10) NOT NULL DEFAULT 10, `level` int NOT NULL DEFAULT 10,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -440,7 +460,7 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `groups` ( CREATE TABLE `groups` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int NOT NULL AUTO_INCREMENT,
`title` text NOT NULL, `title` text NOT NULL,
`announcement` text NOT NULL DEFAULT '', `announcement` text NOT NULL DEFAULT '',
`is_hidden` tinyint(1) NOT NULL DEFAULT 0, `is_hidden` tinyint(1) NOT NULL DEFAULT 0,
@ -455,10 +475,11 @@ CREATE TABLE `groups` (
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `groups_assignments` ( CREATE TABLE `groups_assignments` (
`group_id` int(11) NOT NULL, `group_id` int NOT NULL,
`list_id` int(11) NOT NULL, `list_id` int NOT NULL,
`end_time` datetime NOT NULL, `end_time` datetime NOT NULL,
PRIMARY KEY (`group_id`, `list_id`) PRIMARY KEY (`group_id`, `list_id`),
KEY `list_id` (`list_id`,`group_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -469,9 +490,11 @@ CREATE TABLE `groups_assignments` (
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `groups_users` ( CREATE TABLE `groups_users` (
`group_id` int(11) NOT NULL, `group_id` int NOT NULL,
`username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, `username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`group_id`, `username`) PRIMARY KEY (`group_id`, `username`),
KEY `group_id` (`group_id`, `username`),
KEY `username` (`username`, `group_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -482,23 +505,26 @@ CREATE TABLE `groups_users` (
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `hacks` ( CREATE TABLE `hacks` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`problem_id` int(10) unsigned NOT NULL, `problem_id` int UNSIGNED NOT NULL,
`contest_id` int(10) unsigned DEFAULT NULL, `contest_id` int UNSIGNED DEFAULT NULL,
`submission_id` int(10) unsigned NOT NULL, `submission_id` int UNSIGNED NOT NULL,
`hacker` varchar(20) NOT NULL, `hacker` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`owner` varchar(20) NOT NULL, `owner` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`input` varchar(150) NOT NULL, `input` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL,
`input_type` char(20) NOT NULL, `input_type` char(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`submit_time` datetime NOT NULL, `submit_time` datetime NOT NULL,
`judge_time` datetime DEFAULT NULL, `judge_time` datetime DEFAULT NULL,
`success` tinyint(1) DEFAULT NULL, `success` tinyint(1) DEFAULT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`details` blob NOT NULL, `details` blob NOT NULL,
`is_hidden` tinyint(1) NOT NULL, `is_hidden` tinyint(1) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `submission_id` (`submission_id`), KEY `submission_id` (`submission_id`),
KEY `is_hidden` (`is_hidden`,`problem_id`) KEY `is_hidden` (`is_hidden`,`problem_id`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; KEY `status` (`status`),
KEY `judge_time` (`judge_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
-- --
@ -539,11 +565,14 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `judger_info` ( CREATE TABLE `judger_info` (
`judger_name` varchar(50) NOT NULL, `judger_name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`password` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, `password` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`ip` char(20) NOT NULL, `ip` char(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT '1',
`display_name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`description` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`judger_name`) PRIMARY KEY (`judger_name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
-- --
@ -555,6 +584,26 @@ LOCK TABLES `judger_info` WRITE;
/*!40000 ALTER TABLE `judger_info` ENABLE KEYS */; /*!40000 ALTER TABLE `judger_info` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;
--
-- Table structure for table `meta`
--
CREATE TABLE `meta` (
`name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` json NOT NULL,
`updated_at` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `meta`
--
LOCK TABLES `meta` WRITE;
/*!40000 ALTER TABLE `meta` DISABLE KEYS */;
/*!40000 ALTER TABLE `meta` ENABLE KEYS */;
UNLOCK TABLES;
-- --
-- Table structure for table `problems` -- Table structure for table `problems`
-- --
@ -562,18 +611,20 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `problems` ( CREATE TABLE `problems` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int unsigned NOT NULL AUTO_INCREMENT,
`title` text NOT NULL, `title` text COLLATE utf8mb4_unicode_ci NOT NULL,
`uploader` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, `uploader` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`is_hidden` tinyint(1) NOT NULL DEFAULT '0', `is_hidden` tinyint(1) NOT NULL DEFAULT '0',
`submission_requirement` text, `submission_requirement` mediumtext COLLATE utf8mb4_unicode_ci,
`hackable` tinyint(1) NOT NULL DEFAULT '0', `hackable` tinyint(1) NOT NULL DEFAULT '0',
`extra_config` varchar(500) NOT NULL DEFAULT '{"view_content_type":"ALL","view_details_type":"ALL"}', `extra_config` varchar(500) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '{"view_content_type":"ALL","view_details_type":"ALL"}',
`zan` int(11) NOT NULL DEFAULT '0', `zan` int(11) NOT NULL DEFAULT '0',
`ac_num` int(11) NOT NULL DEFAULT '0', `ac_num` int(11) NOT NULL DEFAULT '0',
`submit_num` int(11) NOT NULL DEFAULT '0', `submit_num` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`) `assigned_to_judger` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'any',
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; PRIMARY KEY (`id`),
KEY `assigned_to_judger` (`assigned_to_judger`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci TABLESPACE `innodb_system`;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
-- --
@ -592,11 +643,11 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `problems_contents` ( CREATE TABLE `problems_contents` (
`id` int(11) NOT NULL, `id` int NOT NULL,
`statement` mediumtext NOT NULL, `statement` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`statement_md` mediumtext NOT NULL, `statement_md` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
-- --
@ -616,9 +667,10 @@ UNLOCK TABLES;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `problems_permissions` ( CREATE TABLE `problems_permissions` (
`username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, `username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`problem_id` int(11) NOT NULL, `problem_id` int NOT NULL,
PRIMARY KEY (`username`,`problem_id`) PRIMARY KEY (`username`,`problem_id`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; KEY `problem_id` (`problem_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
-- --
@ -637,9 +689,10 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `problems_solutions` ( CREATE TABLE `problems_solutions` (
`problem_id` int(11) NOT NULL, `problem_id` int NOT NULL,
`blog_id` int(11) NOT NULL, `blog_id` int NOT NULL,
PRIMARY KEY (`problem_id`, `blog_id`) PRIMARY KEY (`problem_id`, `blog_id`),
KEY `problem_id` (`problem_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -659,8 +712,8 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `problems_tags` ( CREATE TABLE `problems_tags` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int NOT NULL AUTO_INCREMENT,
`problem_id` int(11) NOT NULL, `problem_id` int NOT NULL,
`tag` varchar(30) NOT NULL, `tag` varchar(30) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `problem_id` (`problem_id`), KEY `problem_id` (`problem_id`),
@ -684,7 +737,7 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `lists` ( CREATE TABLE `lists` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int NOT NULL AUTO_INCREMENT,
`title` text NOT NULL, `title` text NOT NULL,
`description` text NOT NULL DEFAULT '', `description` text NOT NULL DEFAULT '',
`is_hidden` tinyint(1) NOT NULL DEFAULT '0', `is_hidden` tinyint(1) NOT NULL DEFAULT '0',
@ -699,9 +752,10 @@ CREATE TABLE `lists` (
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `lists_problems` ( CREATE TABLE `lists_problems` (
`list_id` int(11) NOT NULL, `list_id` int NOT NULL,
`problem_id` int(11) NOT NULL, `problem_id` int NOT NULL,
PRIMARY KEY (`list_id`, `problem_id`) PRIMARY KEY (`list_id`, `problem_id`),
KEY `list_id` (`list_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -712,8 +766,8 @@ CREATE TABLE `lists_problems` (
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `lists_tags` ( CREATE TABLE `lists_tags` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int NOT NULL AUTO_INCREMENT,
`list_id` int(11) NOT NULL, `list_id` int NOT NULL,
`tag` varchar(30) NOT NULL, `tag` varchar(30) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `list_id` (`list_id`), KEY `list_id` (`list_id`),
@ -728,14 +782,14 @@ CREATE TABLE `lists_tags` (
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `search_requests` ( CREATE TABLE `search_requests` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int NOT NULL AUTO_INCREMENT,
`created_at` datetime NOT NULL, `created_at` datetime NOT NULL,
`remote_addr` varchar(50) NOT NULL, `remote_addr` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`type` enum('search','autocomplete') NOT NULL, `type` enum('search','autocomplete') COLLATE utf8mb4_unicode_ci NOT NULL,
`cache_id` int(11) NOT NULL, `cache_id` int NOT NULL,
`q` varchar(100) NOT NULL, `q` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`content` text NOT NULL, `content` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`result` mediumtext NOT NULL, `result` json NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `remote_addr` (`remote_addr`,`created_at`), KEY `remote_addr` (`remote_addr`,`created_at`),
KEY `created_at` (`created_at`) KEY `created_at` (`created_at`)
@ -758,27 +812,42 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `submissions` ( CREATE TABLE `submissions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`problem_id` int(10) unsigned NOT NULL, `problem_id` int UNSIGNED NOT NULL,
`contest_id` int(10) unsigned DEFAULT NULL, `contest_id` int UNSIGNED DEFAULT NULL,
`submit_time` datetime NOT NULL, `submit_time` datetime NOT NULL,
`submitter` varchar(20) NOT NULL, `submitter` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`content` text NOT NULL, `content` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`language` varchar(15) NOT NULL, `language` varchar(15) COLLATE utf8mb4_unicode_ci NOT NULL,
`tot_size` int(11) NOT NULL, `tot_size` int NOT NULL,
`judge_reason` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`judge_time` datetime DEFAULT NULL, `judge_time` datetime DEFAULT NULL,
`result` blob NOT NULL, `judger` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`status` varchar(20) NOT NULL, `result` mediumblob NOT NULL,
`result_error` varchar(20) DEFAULT NULL, `status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`score` int(11) DEFAULT NULL, `result_error` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`used_time` int(11) NOT NULL DEFAULT '0', `score` int DEFAULT NULL,
`used_memory` int(11) NOT NULL DEFAULT '0', `hide_score_to_others` tinyint(1) NOT NULL DEFAULT '0',
`hidden_score` int DEFAULT NULL,
`used_time` int NOT NULL DEFAULT '0',
`used_memory` int NOT NULL DEFAULT '0',
`is_hidden` tinyint(1) NOT NULL, `is_hidden` tinyint(1) NOT NULL,
`status_details` varchar(100) NOT NULL, `status_details` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `is_hidden` (`is_hidden`,`problem_id`), KEY `status` (`status`,`id`),
KEY `score` (`problem_id`, `submitter`, `score`) KEY `result_error` (`result_error`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; KEY `problem_id` (`problem_id`,`id`),
KEY `language` (`language`,`id`),
KEY `language2` (`is_hidden`,`language`,`id`),
KEY `user_score` (`problem_id`,`submitter`,`score`,`id`),
KEY `problem_id2` (`is_hidden`,`problem_id`,`id`),
KEY `id2` (`is_hidden`,`id`),
KEY `problem_score2` (`is_hidden`,`problem_id`,`score`,`id`),
KEY `contest_submission_status` (`contest_id`,`status`),
KEY `submitter2` (`is_hidden`,`submitter`,`id`),
KEY `submitter` (`submitter`,`id`) USING BTREE,
KEY `contest_id` (`contest_id`,`is_hidden`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
-- --
@ -790,6 +859,83 @@ LOCK TABLES `submissions` WRITE;
/*!40000 ALTER TABLE `submissions` ENABLE KEYS */; /*!40000 ALTER TABLE `submissions` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;
--
-- Table structure for table `submissions_history`
--
CREATE TABLE `submissions_history` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`submission_id` int UNSIGNED NOT NULL,
`judge_reason` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`judge_time` datetime DEFAULT NULL,
`judger` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`result` mediumblob NOT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`status_details` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`result_error` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`score` int DEFAULT NULL,
`used_time` int NOT NULL DEFAULT '0',
`used_memory` int NOT NULL DEFAULT '0',
`major` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
KEY `submission_judge_time` (`submission_id`,`judge_time`,`id`),
KEY `submission` (`submission_id`,`id`),
KEY `status_major` (`status`,`major`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `submissions_history`
--
LOCK TABLES `submissions_history` WRITE;
/*!40000 ALTER TABLE `submissions_history` DISABLE KEYS */;
/*!40000 ALTER TABLE `submissions_history` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `system_updates`
--
CREATE TABLE `system_updates` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`time` datetime NOT NULL,
`type` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`target_id` int UNSIGNED NOT NULL,
`message` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `type_id_time` (`type`,`target_id`,`time`),
KEY `type_time` (`type`,`time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `system_updates`
--
LOCK TABLES `system_updates` WRITE;
/*!40000 ALTER TABLE `system_updates` DISABLE KEYS */;
/*!40000 ALTER TABLE `system_updates` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `upgrades`
--
CREATE TABLE `upgrades` (
`name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`status` enum('up','down') COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `upgrades`
--
LOCK TABLES `upgrades` WRITE;
/*!40000 ALTER TABLE `upgrades` DISABLE KEYS */;
/*!40000 ALTER TABLE `upgrades` ENABLE KEYS */;
UNLOCK TABLES;
-- --
-- Table structure for table `user_info` -- Table structure for table `user_info`
-- --
@ -797,32 +943,29 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `user_info` ( CREATE TABLE `user_info` (
`usergroup` char(1) NOT NULL DEFAULT 'U', `usergroup` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'U',
`username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, `username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`usertype` varchar(250) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'student',
`realname` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', `realname` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`school` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', `school` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`usertype` varchar(250) NOT NULL DEFAULT 'student', `email` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(50) NOT NULL, `password` char(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`password` char(32) NOT NULL, `svn_password` char(10) COLLATE utf8mb4_unicode_ci NOT NULL,
`svn_password` char(10) NOT NULL, `qq` bigint NOT NULL DEFAULT '0',
`qq` bigint(20) NOT NULL, `sex` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'U',
`sex` char(1) NOT NULL DEFAULT 'U', `ac_num` int NOT NULL DEFAULT 0,
`ac_num` int(11) NOT NULL,
`register_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `register_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`remote_addr` varchar(50) NOT NULL, `last_login_time` datetime DEFAULT CURRENT_TIMESTAMP,
`http_x_forwarded_for` varchar(50) NOT NULL, `last_visit_time` datetime DEFAULT CURRENT_TIMESTAMP,
`remember_token` char(60) NOT NULL, `expiration_time` datetime DEFAULT NULL,
`motto` varchar(200) NOT NULL, `remote_addr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`last_login` timestamp NOT NULL DEFAULT 0, `http_x_forwarded_for` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`last_visited` timestamp NOT NULL DEFAULT 0, `remember_token` char(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`images_size_limit` int(11) UNSIGNED NOT NULL DEFAULT 104857600, /* 100 MiB */ `motto` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`codeforces_handle` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', `extra` json NOT NULL,
`github` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`website` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`avatar_source` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'gravatar',
PRIMARY KEY (`username`), PRIMARY KEY (`username`),
KEY `ac_num` (`ac_num`,`username`) KEY `ac_num` (`ac_num`,`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
-- --
@ -841,13 +984,13 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `users_images` ( CREATE TABLE `users_images` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`path` varchar(100) NOT NULL, `path` varchar(100) NOT NULL,
`uploader` varchar(20) NOT NULL, `uploader` varchar(20) NOT NULL,
`width` int(11) NOT NULL, `width` int NOT NULL,
`height` int(11) NOT NULL, `height` int NOT NULL,
`upload_time` datetime NOT NULL, `upload_time` datetime NOT NULL,
`size` int(11) NOT NULL, `size` int NOT NULL,
`hash` varchar(100) NOT NULL, `hash` varchar(100) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `uploader` (`uploader`), KEY `uploader` (`uploader`),
@ -874,13 +1017,16 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `user_msg` ( CREATE TABLE `user_msg` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`sender` varchar(20) NOT NULL, `sender` varchar(20) NOT NULL,
`receiver` varchar(20) NOT NULL, `receiver` varchar(20) NOT NULL,
`message` varchar(5000) NOT NULL, `message` varchar(5000) NOT NULL,
`send_time` datetime NOT NULL, `send_time` datetime NOT NULL,
`read_time` datetime DEFAULT NULL, `read_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`),
KEY `sender` (`sender`),
KEY `receiver` (`receiver`),
KEY `read_time` (`receiver`,`read_time`) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -900,13 +1046,14 @@ UNLOCK TABLES;
/*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */; /*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `user_system_msg` ( CREATE TABLE `user_system_msg` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int NOT NULL AUTO_INCREMENT,
`title` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, `title` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`content` text COLLATE utf8mb4_unicode_ci NOT NULL, `content` text COLLATE utf8mb4_unicode_ci NOT NULL,
`receiver` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, `receiver` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`send_time` datetime NOT NULL, `send_time` datetime NOT NULL,
`read_time` datetime DEFAULT NULL, `read_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`),
KEY `receiver` (`receiver`,`read_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;

View File

@ -24,6 +24,8 @@ services:
build: build:
context: ./judger/ context: ./judger/
dockerfile: Dockerfile dockerfile: Dockerfile
args:
- USE_MIRROR=1
container_name: uoj-judger container_name: uoj-judger
restart: always restart: always
stdin_open: true stdin_open: true
@ -44,6 +46,8 @@ services:
build: build:
context: ./ context: ./
dockerfile: web/Dockerfile dockerfile: web/Dockerfile
args:
- USE_MIRROR=1
container_name: uoj-web container_name: uoj-web
restart: always restart: always
stdin_open: true stdin_open: true

View File

@ -1,15 +1,24 @@
FROM ubuntu:22.04 FROM ubuntu:22.04
ARG CLONE_ADDFLAG ARG CLONE_ADDFLAG
ARG USE_MIRROR
ENV USE_MIRROR $USE_MIRROR
SHELL ["/bin/bash", "-c"]
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y vim ntp zip unzip curl wget build-essential fp-compiler python2.7 python3.10 python3-requests libseccomp-dev openjdk-8-jdk openjdk-11-jdk openjdk-17-jdk ENV PKGS="vim ntp zip unzip curl wget build-essential fp-compiler python2.7 python3.10 python3-requests libseccomp-dev openjdk-8-jdk openjdk-11-jdk openjdk-17-jdk"
RUN if [[ "$USE_MIRROR" == "1" ]]; then\
sed -i "s@http://.*archive.ubuntu.com@http://mirrors.tuna.tsinghua.edu.cn@g" /etc/apt/sources.list &&\
sed -i "s@http://.*security.ubuntu.com@http://mirrors.tuna.tsinghua.edu.cn@g" /etc/apt/sources.list ;\
fi &&\
apt-get update &&\
(apt-get install -y --allow-unauthenticated --no-install-recommends -o Dpkg::Options::="--force-overwrite" -o APT::Acquire::Retries="30" --fix-missing $PKGS || apt --fix-broken install -y || apt-get install -y --allow-unauthenticated --no-install-recommends -o Dpkg::Options::="--force-overwrite" -o APT::Acquire::Retries="30" --fix-missing $PKGS)
ADD . /opt/uoj_judger ADD . /opt/uoj_judger
WORKDIR /opt/uoj_judger WORKDIR /opt/uoj_judger
# Install environment and set startup script # Install environment and set startup script
RUN sh install.sh -p && echo "\ RUN sh install.sh -p && echo -e "\
#!/bin/sh\n\ #!/bin/sh\n\
if [ ! -f \"/opt/uoj_judger/.conf.json\" ]; then\n\ if [ ! -f \"/opt/uoj_judger/.conf.json\" ]; then\n\
cd /opt/uoj_judger && sh install.sh -i\n\ cd /opt/uoj_judger && sh install.sh -i\n\

View File

@ -35,12 +35,15 @@ const std::vector<std::pair<const char *, const char *>> suffix_search_list = {
{".code" , "" }, {".code" , "" },
{"20.cpp" , "C++20" }, {"20.cpp" , "C++20" },
{"17.cpp" , "C++17" }, {"17.cpp" , "C++17" },
{"14.cpp" , "C++14" }, {"14.cpp" , "C++" },
{"11.cpp" , "C++11" }, {"11.cpp" , "C++11" },
{".cpp" , "C++" }, {"03.cpp" , "C++03" },
{"98.cpp" , "C++98" },
{".cpp" , "C++" },
{".c" , "C" }, {".c" , "C" },
{".pas" , "Pascal" }, {".pas" , "Pascal" },
{"2.7.py" , "Python2.7"}, {"2.7.py" , "Python2.7"},
{"2.py" , "Python2.7"},
{".py" , "Python3" }, {".py" , "Python3" },
{"7.java" , "Java7" }, {"7.java" , "Java7" },
{"8.java" , "Java8" }, {"8.java" , "Java8" },

View File

@ -1,18 +1,30 @@
FROM ubuntu:22.04 FROM ubuntu:22.04
ARG CLONE_ADDFLAG ARG CLONE_ADDFLAG
ARG USE_MIRROR
ENV USE_MIRROR $USE_MIRROR
SHELL ["/bin/bash", "-c"]
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
RUN dpkg -s gnupg 2>/dev/null || (apt-get update && apt-get install -y gnupg) &&\ ENV PKGS="php7.4 php7.4-yaml php7.4-xml php7.4-dev php7.4-zip php7.4-mysql php7.4-mbstring php7.4-gd php7.4-imagick libseccomp-dev git vim ntp zip unzip curl wget apache2 libapache2-mod-xsendfile php-pear mysql-client build-essential fp-compiler re2c libseccomp-dev libyaml-dev python2.7 python3.10 python3-requests openjdk-8-jdk openjdk-11-jdk openjdk-17-jdk"
echo "deb http://ppa.launchpad.net/ondrej/php/ubuntu jammy main" | tee /etc/apt/sources.list.d/ondrej-php.list && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4F4EA0AAE5267A6C &&\ RUN if [[ "$USE_MIRROR" == "1" ]]; then\
apt-get update --allow-unauthenticated &&\ sed -i "s@http://.*archive.ubuntu.com@https://mirrors.aliyun.com@g" /etc/apt/sources.list &&\
apt-get install -y --allow-unauthenticated -o Dpkg::Options::="--force-overwrite" php7.4 php7.4-yaml php7.4-xml php7.4-dev php7.4-zip php7.4-mysql php7.4-mbstring php7.4-gd php7.4-imagick libseccomp-dev git vim ntp zip unzip curl wget libapache2-mod-xsendfile mysql-server php-pear cmake fp-compiler re2c libyaml-dev python2.7 python3.10 python3-requests openjdk-8-jdk openjdk-11-jdk openjdk-17-jdk sed -i "s@http://.*security.ubuntu.com@https://mirrors.aliyun.com@g" /etc/apt/sources.list ;\
fi &&\
apt-get -o "Acquire::https::Verify-Peer=false" update && apt-get -o "Acquire::https::Verify-Peer=false" install -y --no-install-recommends gnupg ca-certificates &&\
echo "deb http://ppa.launchpad.net/ondrej/php/ubuntu jammy main" | tee /etc/apt/sources.list.d/ondrej-php.list && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4F4EA0AAE5267A6C &&\
if [[ "$USE_MIRROR" == "1" ]]; then\
find /etc/apt/sources.list.d/ -type f -name "*.list" -exec sed -i.bak -r 's#deb(-src)?\s*http(s)?://ppa.launchpad.net#deb\1 https://launchpad.proxy.ustclug.org#ig' {} \; &&\
find /etc/apt/sources.list.d/ -type f -name "*.list" -exec sed -i.bak -r 's#deb(-src)?\s*http(s)?://ppa.launchpadcontent.net#deb\1 https://launchpad.proxy.ustclug.org#ig' {} \; ;\
fi &&\
apt-get update --allow-unauthenticated &&\
(apt-get install -y --allow-unauthenticated --no-install-recommends -o Dpkg::Options::="--force-overwrite" -o APT::Acquire::Retries="30" --fix-missing $PKGS || apt-get install --fix-broken -y -o APT::Acquire::Retries="30")
ADD . /opt/uoj ADD . /opt/uoj
WORKDIR /opt/uoj WORKDIR /opt/uoj
# Install environment and set startup script # Install environment and set startup script
RUN sh web/install.sh -p && echo "\ RUN sh web/install.sh -p && echo -e "\
#!/bin/sh\n\ #!/bin/sh\n\
if [ ! -f \"/var/uoj_data/.UOJSetupDone\" ]; then\n\ if [ ! -f \"/var/uoj_data/.UOJSetupDone\" ]; then\n\
cd /opt/uoj/web && sh install.sh -i\n\ cd /opt/uoj/web && sh install.sh -i\n\

View File

@ -21,12 +21,12 @@ return [
'main' => [ 'main' => [
'protocol' => 'http', 'protocol' => 'http',
'host' => '_httpHost_', 'host' => '_httpHost_',
'port' => 80 'port' => '80/443'
], ],
'blog' => [ 'blog' => [
'protocol' => 'http', 'protocol' => 'http',
'host' => '_httpHost_', 'host' => '_httpHost_',
'port' => 80 'port' => '80/443'
] ]
], ],
'security' => [ 'security' => [
@ -54,7 +54,6 @@ return [
], ],
'switch' => [ 'switch' => [
'blog-domain-mode' => 3, 'blog-domain-mode' => 3,
'force-login' => true,
'open-register' => false 'open-register' => false
] ]
]; ];

View File

@ -1,101 +1,103 @@
<?php <?php
if (!Auth::check()) { requireLib('bootstrap5');
redirectToLogin(); requirePHPLib('form');
}
if (!isNormalUser($myUser)) { Auth::check() || redirectToLogin();
become403Page(); isSuperUser(Auth::user()) || UOJResponse::page403();
}
requireLib('bootstrap5'); $time_form = new UOJBs4Form('time');
requirePHPLib('form'); $time_form->addVInput(
'name',
if (!isSuperUser($myUser)) { 'text',
become403Page(); '比赛标题',
} 'New Contest',
$time_form = new UOJForm('time'); function ($name, &$vdata) {
$time_form->addVInput( if ($name == '') {
'name', 'text', '比赛标题', 'New Contest', return '标题不能为空';
function($name, &$vdata) { }
if ($name == '') {
return '标题不能为空';
}
if (strlen($name) > 100) {
return '标题过长';
}
$name = HTML::escape($name); if (strlen($name) > 100) {
return '标题过长';
}
if ($name === '') { $name = HTML::escape($name);
return '无效编码';
}
$vdata['name'] = $name; if ($name === '') {
return '无效编码';
}
return ''; $vdata['name'] = $name;
},
null
);
$time_form->addVInput(
'start_time', 'text', '开始时间', date("Y-m-d H:i:s"),
function($str, &$vdata) {
try {
$vdata['start_time'] = new DateTime($str);
} catch (Exception $e) {
return '无效时间格式';
}
return '';
},
null
);
$time_form->addVInput(
'last_min', 'text', '时长(单位:分钟)', 180,
function($str, &$vdata) {
if (!validateUInt($str)) {
return '必须为一个整数';
}
$vdata['last_min'] = $str; return '';
},
null
);
$time_form->addVInput(
'start_time',
'text',
'开始时间',
date("Y-m-d H:i:s"),
function ($str, &$vdata) {
try {
$vdata['start_time'] = new DateTime($str);
} catch (Exception $e) {
return '无效时间格式';
}
return '';
},
null
);
$time_form->addVInput(
'last_min',
'text',
'时长(单位:分钟)',
180,
function ($str, &$vdata) {
if (!validateUInt($str)) {
return '必须为一个整数';
}
return ''; $vdata['last_min'] = $str;
},
null return '';
); },
$time_form->handle = function(&$vdata) { null
$start_time_str = $vdata['start_time']->format('Y-m-d H:i:s'); );
$esc_name = DB::escape($vdata['name']); $time_form->handle = function (&$vdata) {
$esc_last_min = DB::escape($vdata['last_min']); $start_time_str = $vdata['start_time']->format('Y-m-d H:i:s');
DB::query("insert into contests (name, start_time, last_min, status) values ('$esc_name', '$start_time_str', $esc_last_min, 'unfinished')"); DB::insert([
}; "insert into contests",
$time_form->succ_href = "/contests"; "(name, start_time, last_min, status)", "values",
$time_form->runAtServer(); DB::tuple([$vdata['name'], $start_time_str, $vdata['last_min'], 'unfinished'])
?> ]);
};
$time_form->succ_href = "/contests";
$time_form->runAtServer();
?>
<?php echoUOJPageHeader('添加比赛') ?> <?php echoUOJPageHeader('添加比赛') ?>
<div class="row"> <div class="row">
<div class="col-lg-9"> <!-- left col -->
<div class="col-lg-9">
<div class="card card-default mb-2">
<div class="card-body">
<div class="card card-default mb-2"> <h1 class="card-title">添加比赛</h1>
<div class="card-body">
<h1 class="h2 card-title">添加比赛</h1> <div class="w-full" style="max-width: 400px">
<?php $time_form->printHTML(); ?>
</div>
<div class="w-full" style="max-width: 400px"> </div>
<?php $time_form->printHTML(); ?> </div>
</div> </div>
<!-- end left col -->
</div>
</div>
</div>
<!-- right col -->
<aside class="col-lg-3 mt-3 mt-lg-0">
<?php uojIncludeView('sidebar', array()) ?>
</aside>
<!-- right col -->
<aside class="col-lg-3 mt-3 mt-lg-0">
<?php uojIncludeView('sidebar') ?>
</aside>
</div> </div>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,73 +1,57 @@
<?php <?php
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { requireLib('bootstrap5');
redirectToLogin(); requirePHPLib('form');
} ?>
<?php echoUOJPageHeader(UOJLocale::get('announcements')) ?>
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { <div class="row">
become403Page(); <!-- left col -->
} <div class="col-lg-9">
<h1>
<?= UOJLocale::get('announcements') ?>
</h1>
requireLib('bootstrap5'); <?php
requirePHPLib('form'); echoLongTable(
['blogs.id', 'poster', 'title', 'post_time', 'zan', 'level'],
function echoBlogCell($blog) { 'important_blogs, blogs',
$level = $blog['level']; [
'is_hidden' => 0,
switch ($level) { 'important_blogs.blog_id' => DB::raw('blogs.id')
case 0: ],
$level_str = ''; 'order by level desc, important_blogs.blog_id desc',
break; <<<EOD
case 1:
$level_str = '<span style="color:red">[三级置顶]</span> ';
break;
case 2:
$level_str = '<span style="color:red">[二级置顶]</span> ';
break;
case 3:
$level_str = '<span style="color:red">[一级置顶]</span> ';
break;
}
echo '<tr>';
echo '<td>' . $level_str . getBlogLink($blog['id']) . '</td>';
echo '<td>' . getUserLink($blog['poster']) . '</td>';
echo '<td>' . $blog['post_time'] . '</td>';
echo '</tr>';
}
$header = <<<EOD
<tr> <tr>
<th width="60%">标题</th> <th width="60%">标题</th>
<th width="20%">发表者</th> <th width="20%">发表者</th>
<th width="20%">发表日期</th> <th width="20%">发表日期</th>
</tr> </tr>
EOD; EOD,
$config = [ function ($info) {
'page_len' => 40, $blog = new UOJBlog($info);
'div_classes' => ['card', 'my-3'],
'table_classes' => ['table', 'uoj-table', 'mb-0'],
];
?>
<?php echoUOJPageHeader(UOJLocale::get('announcements')) ?>
<div class="row"> echo '<tr>';
<!-- left col --> echo '<td>' . $blog->getLink(['show_level' => true, 'show_new_tag' => true]) . '</td>';
<div class="col-lg-9"> echo '<td>' . getUserLink($blog->info['poster']) . '</td>';
echo '<td>' . $blog->info['post_time'] . '</td>';
echo '</tr>';
},
[
'page_len' => 40,
'div_classes' => ['card', 'my-3'],
'table_classes' => ['table', 'uoj-table', 'mb-0'],
]
);
?>
<h1 class="h2"> </div>
<?= UOJLocale::get('announcements') ?> <!-- end left col -->
</h1>
<?php echoLongTable(array('blogs.id', 'poster', 'title', 'post_time', 'zan', 'level'), 'important_blogs, blogs', 'is_hidden = 0 and important_blogs.blog_id = blogs.id', 'order by level desc, important_blogs.blog_id desc', $header, 'echoBlogCell', $config); ?>
</div>
<!-- end left col -->
<!-- right col -->
<aside class="col-lg-3 mt-3 mt-lg-0">
<?php uojIncludeView('sidebar', array()) ?>
</aside>
<!-- end right col -->
<!-- right col -->
<aside class="col-lg-3 mt-3 mt-lg-0">
<?php uojIncludeView('sidebar') ?>
</aside>
<!-- end right col -->
</div> </div>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,15 +1,6 @@
<?php <?php
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) {
redirectToLogin();
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
become403Page(); UOJBlog::init(UOJRequest::get('id')) || UOJResponse::page404();
}
if (!validateUInt($_GET['id']) || !($blog = queryBlog($_GET['id']))) { redirectTo(HTML::blog_url(UOJBlog::info('poster'), '/post/' . UOJBlog::info('id'), ['escape' => false]));
become404Page();
}
redirectTo(HTML::blog_url($blog['poster'], '/post/'.$_GET['id'] . ($_GET['sub'] ?: '')));
?>

View File

@ -1,83 +1,80 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requirePHPLib('form'); requirePHPLib('form');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin(); ?>
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) {
become403Page();
}
?>
<?php echoUOJPageHeader(UOJLocale::get('blogs')) ?> <?php echoUOJPageHeader(UOJLocale::get('blogs')) ?>
<div class="row"> <div class="row">
<!-- left col -->
<div class="col-lg-9">
<!-- title container -->
<div class="d-flex flex-wrap justify-content-between align-items-center">
<h1>
<?= UOJLocale::get("blogs overview") ?>
</h1>
<!-- left col --> <?php if (Auth::check()) : ?>
<div class="col-lg-9"> <div class="text-end">
<div class="btn-group">
<a href="<?= HTML::blog_url(Auth::id(), '/') ?>" class="btn btn-secondary btn-sm">
我的博客首页
</a>
<a href="<?= HTML::blog_url(Auth::id(), '/post/new/write') ?>" class="btn btn-primary btn-sm">
<i class="bi bi-pencil"></i>
写新博客
</a>
</div>
</div>
<?php endif ?>
</div>
<!-- end title container -->
<!-- title container --> <?php
<div class="d-flex flex-wrap justify-content-between align-items-center"> echoLongTable(
['id', 'poster', 'title', 'post_time', 'zan', 'is_hidden'],
<h1 class="h2"> 'blogs',
<?= UOJLocale::get("blogs overview") ?> isSuperUser($myUser) ? "1" : "is_hidden = 0 or poster = '{$myUser['username']}'",
</h1> 'order by post_time desc',
<<<EOD
<div class="text-end">
<div class="btn-group">
<a href="<?= HTML::blog_url(Auth::id(), '/') ?>" class="btn btn-secondary btn-sm">
我的博客首页
</a>
<a href="<?= HTML::blog_url(Auth::id(), '/post/new/write')?>" class="btn btn-primary btn-sm">
<i class="bi bi-pencil"></i>
写新博客
</a>
</div>
</div>
</div>
<!-- end title container -->
<?php
echoLongTable(
['id', 'poster', 'title', 'post_time', 'zan', 'is_hidden'],
'blogs',
isSuperUser($myUser) ? "1" : "is_hidden = 0 or poster = '{$myUser['username']}'",
'order by post_time desc',
<<<EOD
<tr> <tr>
<th width="60%">标题</th> <th>标题</th>
<th width="20%">发表者</th> <th style="width:200px">发表者</th>
<th width="20%">发表日期</th> <th style="width:200px">发表日期</th>
<th style="width:50px" class="text-center">评价</th>
</tr> </tr>
EOD, EOD,
function($blog) { function ($info) {
echo '<tr>'; $blog = new UOJBlog($info);
echo '<td>';
echo getBlogLink($blog['id']);
if ($blog['is_hidden']) {
echo ' <span class="badge text-bg-danger"><i class="bi bi-eye-slash-fill"></i> ', UOJLocale::get('hidden'), '</span> ';
}
echo '</td>';
echo '<td>' . getUserLink($blog['poster']) . '</td>';
echo '<td>' . $blog['post_time'] . '</td>';
echo '</tr>';
},
[
'page_len' => 10,
'div_classes' => ['card', 'my-3', 'table-responsive'],
'table_classes' => ['table', 'uoj-table', 'mb-0'],
]
);
?>
</div> echo '<tr>';
echo '<td>';
echo $blog->getLink();
if ($blog->info['is_hidden']) {
echo ' <span class="badge text-bg-danger"><i class="bi bi-eye-slash-fill"></i> ', UOJLocale::get('hidden'), '</span> ';
}
echo '</td>';
echo '<td>' . getUserLink($blog->info['poster']) . '</td>';
echo '<td>' . $blog->info['post_time'] . '</td>';
echo '<td class="text-center">' . ClickZans::getCntBlock($blog->info['zan']) . '</td>';
echo '</tr>';
},
[
'page_len' => 10,
'div_classes' => ['card', 'my-3', 'table-responsive'],
'table_classes' => ['table', 'uoj-table', 'mb-0'],
]
);
?>
<!-- right col --> </div>
<aside class="col-lg-3 mt-3 mt-lg-0">
<?php uojIncludeView('sidebar') ?> <!-- right col -->
</aside> <aside class="col-lg-3 mt-3 mt-lg-0">
<!-- end right col --> <?php uojIncludeView('sidebar') ?>
</aside>
<!-- end right col -->
</div> </div>

View File

@ -9,5 +9,3 @@ $_SESSION['phrase'] = $builder->getPhrase();
header('Content-Type: image/jpeg'); header('Content-Type: image/jpeg');
$builder->build()->output(); $builder->build()->output();
?>

View File

@ -0,0 +1,3 @@
<?php
die(json_encode(UOJNotice::fetch(UOJTime::str2time(UOJRequest::post('last_time')))));

View File

@ -1,69 +1,24 @@
<?php <?php
function validateZan() {
if (!validateUInt($_POST['id'])) { function validateZan() {
return false; if (!validateUInt($_POST['id']))
} return false;
if (!validateInt($_POST['delta'])) { if (!validateInt($_POST['delta']))
return false; return false;
} if ($_POST['delta'] != 1 && $_POST['delta'] != -1)
if ($_POST['delta'] != 1 && $_POST['delta'] != -1) { return false;
return false; if (!ClickZans::getTable($_POST['type']))
} return false;
if ($_POST['type'] != 'B' && $_POST['type'] != 'BC' && $_POST['type'] != 'P' && $_POST['type'] != 'C') { return true;
return false; }
} if (!validateZan()) {
return true; die('<div class="text-danger">failed</div>');
} }
if (!validateZan()) { if (!Auth::check()) {
die('<div class="text-danger">failed</div>'); die('<div class="text-danger">please <a href="' . HTML::url('/login') . '">log in</a></div>');
} }
if ($myUser == null) { if (!ClickZans::canClickZan($_POST['id'], $_POST['type'], Auth::user())) {
die('<div class="text-danger">please <a href="'.HTML::url('/login').'">log in</a></div>'); die('<div class="text-danger">no permission</div>');
} }
$id = $_POST['id']; die(ClickZans::click($_POST['id'], $_POST['type'], Auth::user(), $_POST['delta'], $_POST['show-text']));
$delta = $_POST['delta'];
$type = $_POST['type'];
$show_text = isset($_POST['show-text']) && $_POST['show-text'] != 'false';
switch ($type) {
case 'B':
$table_name = 'blogs';
break;
case 'BC':
$table_name = 'blogs_comments';
break;
case 'P':
$table_name = 'problems';
break;
case 'C':
$table_name = 'contests';
break;
}
$cur = queryZanVal($id, $type, $myUser);
if ($cur != $delta) {
$row = DB::selectFirst("select zan from $table_name where id = $id");
if ($row == null) {
die('<div class="text-danger">failed</div>');
}
$cur += $delta;
if ($cur == 0) {
DB::query("delete from click_zans where username = '{$myUser['username']}' and type = '$type' and target_id = $id");
} elseif ($cur != $delta) {
DB::query("update click_zans set val = '$cur' where username = '{$myUser['username']}' and type = '$type' and target_id = $id");
} else {
DB::query("insert into click_zans (username, type, target_id, val) values ('{$myUser['username']}', '$type', $id, $cur)");
}
$cnt = $row['zan'] + $delta;
DB::query("update $table_name set zan = $cnt where id = $id");
} else {
$row = DB::selectFirst("select zan from $table_name where id = $id");
if ($row == null) {
die('<div class="text-danger">failed</div>');
}
$cnt = $row['zan'];
}
?>
<?= getClickZanBlock($type, $id, $cnt, $cur, $show_text) ?>

View File

@ -1,77 +1,65 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requirePHPLib('form'); requirePHPLib('form');
if (!validateUInt($_GET['id']) || !($contest = queryContest($_GET['id']))) { Auth::check() || redirectToLogin();
become404Page(); UOJContest::init(UOJRequest::get('id')) || UOJResponse::page404();
} UOJContest::cur()->userCanParticipateNow(Auth::user()) || UOJResponse::page403();
UOJContest::cur()->userHasMarkedParticipated(Auth::user()) && redirectTo(UOJContest::cur()->getUri());
genMoreContestInfo($contest); $confirm_form = new UOJBs4Form('confirm');
$confirm_form->submit_button_config['class_str'] = 'btn btn-primary mt-3';
if (!Auth::check()) { $confirm_form->submit_button_config['text'] = '我已核对信息,确认参加比赛';
redirectToLogin(); $confirm_form->handle = function () {
} elseif (!hasRegistered($myUser, $contest)) { UOJContest::cur()->markUserAsParticipated(Auth::user());
redirectTo("/contest/{$contest['id']}/register"); };
} elseif ($contest['cur_progress'] < CONTEST_IN_PROGRESS) { $confirm_form->succ_href = '/contest/' . UOJContest::info('id');
redirectTo('/contests'); $confirm_form->runAtServer();
} elseif (hasParticipated($myUser, $contest) || $contest['cur_progress'] > CONTEST_IN_PROGRESS) { ?>
redirectTo("/contest/{$contest['id']}");
}
$confirm_form = new UOJForm('confirm'); <?php echoUOJPageHeader('确认参赛 - ' . UOJContest::info('name')) ?>
$confirm_form->submit_button_config['class_str'] = 'btn btn-primary';
$confirm_form->submit_button_config['margin_class'] = 'mt-3';
$confirm_form->submit_button_config['text'] = '我已核对信息,确认参加比赛';
$confirm_form->handle = function() use ($myUser, $contest) {
DB::update("update contests_registrants set has_participated = 1 where username = '{$myUser['username']}' and contest_id = {$contest['id']}");
};
$confirm_form->succ_href = "/contest/{$contest['id']}";
$confirm_form->runAtServer();
?>
<?php echoUOJPageHeader('确认参赛 - ' . HTML::stripTags($contest['name'])) ?>
<div class="card mw-100 mx-auto" style="width:800px"> <div class="card mw-100 mx-auto" style="width:800px">
<div class="card-body"> <div class="card-body">
<h1 class="h2 card-title text-center mb-3">确认参赛</h1> <h1 class="card-title text-center mb-3">确认参赛</h1>
<p class="card-text text-center">您即将参加比赛 <b><?= $contest['name'] ?></b>”,请在正式参赛前仔细核对以下比赛信息:</p> <p class="card-text text-center">您即将参加比赛 <b><?= UOJContest::info('name') ?></b>”,请在正式参赛前仔细核对以下比赛信息:</p>
<div class="table-responsive mx-auto" style="width:500px"> <div class="table-responsive mx-auto" style="width:500px">
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th style="width:40%"></th> <th style="width:40%"></th>
<th style="width:60%"></th> <th style="width:60%"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td class="text-center">比赛名称</td> <td class="text-center">比赛名称</td>
<td><?= $contest['name'] ?></td> <td><?= UOJContest::info('name') ?></td>
</tr> </tr>
<tr> <tr>
<td class="text-center">参赛选手</td> <td class="text-center">参赛选手</td>
<td><?= getUserLink($myUser['username']) ?></td> <td><?= getUserLink(Auth::id()) ?></td>
</tr> </tr>
<tr> <tr>
<td class="text-center">开始时间</td> <td class="text-center">开始时间</td>
<td><?= $contest['start_time_str'] ?></td> <td><?= UOJContest::info('start_time_str') ?></td>
</tr> </tr>
<tr> <tr>
<td class="text-center">结束时间</td> <td class="text-center">结束时间</td>
<td><?= $contest['end_time_str'] ?></td> <td><?= UOJContest::info('end_time_str') ?></td>
</tr> </tr>
<tr> <tr>
<td class="text-center">比赛赛制</td> <td class="text-center">比赛赛制</td>
<td><?= $contest['extra_config']['contest_type'] ?: 'OI' ?></td> <td><?= UOJContest::cur()->basicRule() ?></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div>
<?php $confirm_form->printHTML() ?>
</div> </div>
<?php $confirm_form->printHTML() ?>
</div>
</div> </div>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,186 +1,167 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requirePHPLib('form'); requirePHPLib('form');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin(); UOJContest::init(UOJRequest::get('id')) || UOJResponse::page404();
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { $contest = UOJContest::info();
become403Page();
}
if (!validateUInt($_GET['id']) || !($contest = queryContest($_GET['id']))) { $is_manager = UOJContest::cur()->userCanManage(Auth::user());
become404Page(); $show_ip = isSuperUser(Auth::user());
}
genMoreContestInfo($contest); if ($is_manager) {
$add_new_contestant_form = new UOJBs4Form('add_new_contestant_form');
$add_new_contestant_form->addInput(
'new_username',
'text',
'用户名',
'',
function ($username, &$vdata) {
$user = UOJUser::query($username);
if (isSuperUser($myUser)) { if (!$user) {
$add_new_contestant_form = new UOJForm('add_new_contestant_form'); return '用户不存在';
$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'];
DB::query("replace into contests_registrants (username, contest_id, has_participated) values ('{$username}', {$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 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, contest_id, has_participated) values ('{$user['username']}', {$contest['id']}, 0)");
} }
updateContestPlayerNum($contest); if (UOJContest::cur()->userHasRegistered($user)) {
}; return '该用户已经报名';
$add_group_to_contest_form->runAtServer(); }
$remove_user_from_contest_form = new UOJForm('remove_user_from_contest'); $vdata['user'] = $user;
$remove_user_from_contest_form->addInput('remove_username', 'text', '用户名', '',
function ($x) {
global $contest;
if (!validateUsername($x)) {
return '用户名不合法';
}
$user = queryUser($x); return '';
if (!$user) { },
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 (&$vdata) {
UOJContest::cur()->userRegister($vdata['user']);
};
$add_new_contestant_form->runAtServer();
if (!hasRegistered($user, $contest)) { $add_group_to_contest_form = new UOJBs4Form('add_group_to_contest');
return '该用户未报名'; $add_group_to_contest_form->addInput(
} 'group_id',
'text',
'小组 ID',
'',
function ($group_id, &$vdata) {
if (!validateUInt($group_id)) {
return '小组 ID 不合法';
}
$group = queryGroup($group_id);
if (!$group) {
return '小组不存在';
}
return ''; $vdata['group_id'] = $group_id;
},
null
);
$remove_user_from_contest_form->submit_button_config['align'] = 'compressed';
$remove_user_from_contest_form->submit_button_config['text'] = '移除该用户';
$remove_user_from_contest_form->submit_button_config['class_str'] = 'mt-2 btn btn-danger';
$remove_user_from_contest_form->handle = function() {
global $contest;
$username = $_POST['remove_username'];
DB::query("delete from contests_registrants where username = '{$username}' and contest_id = {$contest['id']}"); return '';
updateContestPlayerNum($contest); },
}; null
$remove_user_from_contest_form->runAtServer(); );
$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 (&$vdata) {
$users = queryGroupUsers($vdata['group_id']);
$force_set_user_participated_form = new UOJForm('force_set_user_participated'); foreach ($users as $user) {
$force_set_user_participated_form->addInput('force_set_username', 'text', '用户名', '', UOJContest::cur()->userRegister($user);
function ($x) { }
global $contest; };
$add_group_to_contest_form->runAtServer();
if (!validateUsername($x)) { $remove_user_from_contest_form = new UOJBs4Form('remove_user_from_contest');
return '用户名不合法'; $remove_user_from_contest_form->addInput(
} 'remove_username',
'text',
'用户名',
'',
function ($username, &$vdata) {
$user = UOJUser::query($username);
$user = queryUser($x); if (!$user) {
if (!$user) { return '用户不存在';
return '用户不存在'; }
}
if (!hasRegistered($user, $contest)) { if (!UOJContest::cur()->userHasRegistered($user)) {
return '该用户未报名'; return '该用户未报名';
} }
return ''; $vdata['user'] = $user;
},
null
);
$force_set_user_participated_form->submit_button_config['align'] = 'compressed';
$force_set_user_participated_form->submit_button_config['text'] = '强制参赛';
$force_set_user_participated_form->submit_button_config['class_str'] = 'mt-2 btn btn-warning';
$force_set_user_participated_form->handle = function() {
global $contest;
$username = $_POST['force_set_username'];
DB::query("update contests_registrants set has_participated = 1 where username = '{$username}' and contest_id = {$contest['id']}"); return '';
updateContestPlayerNum($contest); },
}; null
$force_set_user_participated_form->runAtServer(); );
} $remove_user_from_contest_form->submit_button_config['align'] = 'compressed';
$remove_user_from_contest_form->submit_button_config['text'] = '移除该用户';
$remove_user_from_contest_form->submit_button_config['class_str'] = 'mt-2 btn btn-danger';
$remove_user_from_contest_form->handle = function (&$vdata) {
UOJContest::cur()->userUnregister($vdata['user']);
};
$remove_user_from_contest_form->runAtServer();
$has_contest_permission = hasContestPermission($myUser, $contest); $force_set_user_participated_form = new UOJBs4Form('force_set_user_participated');
$show_ip = $has_contest_permission; $force_set_user_participated_form->addInput(
'force_set_username',
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) { 'text',
$iHasRegistered = $myUser != null && hasRegistered($myUser, $contest); '用户名',
'',
if ($iHasRegistered) { function ($username, &$vdata) {
$unregister_form = new UOJForm('unregister'); $user = UOJUser::query($username);
$unregister_form->handle = function() {
global $myUser, $contest; if (!$user) {
DB::query("delete from contests_registrants where username = '{$myUser['username']}' and contest_id = {$contest['id']}"); return '用户不存在';
updateContestPlayerNum($contest); }
if (!UOJContest::cur()->userHasRegistered($user)) {
return '该用户未报名';
}
$vdata['user'] = $user;
return '';
},
null
);
$force_set_user_participated_form->submit_button_config['align'] = 'compressed';
$force_set_user_participated_form->submit_button_config['text'] = '强制参赛';
$force_set_user_participated_form->submit_button_config['class_str'] = 'mt-2 btn btn-warning';
$force_set_user_participated_form->handle = function (&$vdata) {
UOJContest::cur()->markUserAsParticipated($vdata['user']);
};
$force_set_user_participated_form->runAtServer();
}
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {
$iHasRegistered = UOJContest::cur()->userHasRegistered(Auth::user());
if ($iHasRegistered) {
if ($iHasRegistered && UOJContest::cur()->freeRegistration()) {
$unregister_form = new UOJBs4Form('unregister');
$unregister_form->handle = function () {
UOJContest::cur()->userUnregister(Auth::user());
}; };
$unregister_form->submit_button_config['align'] = 'right';
$unregister_form->submit_button_config['class_str'] = 'btn btn-danger btn-xs'; $unregister_form->submit_button_config['class_str'] = 'btn btn-danger btn-xs';
$unregister_form->submit_button_config['text'] = '取消报名'; $unregister_form->submit_button_config['text'] = '取消报名';
$unregister_form->succ_href = "/contests"; $unregister_form->succ_href = "/contests";
$unregister_form->runAtServer(); $unregister_form->runAtServer();
} }
} }
?> }
?>
<?php echoUOJPageHeader(HTML::stripTags($contest['name']) . ' - ' . UOJLocale::get('contests::contest registrants')) ?> <?php echoUOJPageHeader(HTML::stripTags($contest['name']) . ' - ' . UOJLocale::get('contests::contest registrants')) ?>
<h1 class="h2 text-center"> <h1 class="text-center">
<?= $contest['name'] ?> <?= $contest['name'] ?>
</h1> </h1>
<?php if ($contest['cur_progress'] == CONTEST_NOT_STARTED): ?> <?php if ($contest['cur_progress'] == CONTEST_NOT_STARTED) : ?>
<?php if ($iHasRegistered): ?> <?php if ($iHasRegistered) : ?>
<div class="row"> <div class="row">
<div class="col-6"> <div class="col-6">
<a class="text-decoration-none text-success">已报名</a> <a class="text-decoration-none text-success">已报名</a>
@ -189,64 +170,64 @@
<?php $unregister_form->printHTML(); ?> <?php $unregister_form->printHTML(); ?>
</div> </div>
</div> </div>
<?php else: ?> <?php else : ?>
<div>当前尚未报名,您可以 <a class="text-decoration-none text-danger" href="/contest/<?= $contest['id'] ?>/register">报名</a></div> <div>当前尚未报名,您可以 <a class="text-decoration-none text-danger" href="/contest/<?= $contest['id'] ?>/register">报名</a></div>
<?php endif ?> <?php endif ?>
<div class="mt-2"></div> <div class="mt-2"></div>
<?php endif ?> <?php endif ?>
<?php <?php
$header_row = '<tr><th>#</th><th>'.UOJLocale::get('username').'</th>'; $header_row = '<tr><th>#</th><th>' . UOJLocale::get('username') . '</th>';
if ($show_ip) { if ($show_ip) {
$header_row .= '<th>remote_addr</th><th>http_x_forwarded_for</th>'; $header_row .= '<th>remote_addr</th><th>http_x_forwarded_for</th>';
} }
if ($has_contest_permission) { if ($is_manager) {
$header_row .= '<th>是否参赛</th>'; $header_row .= '<th>是否参赛</th>';
} }
$header_row .= '</tr>'; $header_row .= '</tr>';
echoLongTable( echoLongTable(
['*'], ['*'],
'contests_registrants', 'contests_registrants',
"contest_id = {$contest['id']}", ['contest_id' => $contest['id']],
'order by username desc', 'order by username desc',
$header_row, $header_row,
function($contestant, $num) use ($myUser, $has_contest_permission, $show_ip, $has_participated) { function ($contestant, $num) use ($is_manager, $show_ip) {
$user = queryUser($contestant['username']); $user = UOJUser::query($contestant['username']);
echo '<tr>'; echo '<tr>';
echo '<td>'.$num.'</td>'; echo '<td>' . $num . '</td>';
echo '<td>'.getUserLink($contestant['username']).'</td>'; echo '<td>' . getUserLink($user['username']) . '</td>';
if ($show_ip) { if ($show_ip) {
echo '<td>'.$user['remote_addr'].'</td>'; echo '<td>' . $user['remote_addr'] . '</td>';
echo '<td>'.$user['http_x_forwarded_for'].'</td>'; echo '<td>' . $user['http_x_forwarded_for'] . '</td>';
} }
if ($has_contest_permission) { if ($is_manager) {
echo '<td>'.($contestant['has_participated'] ? 'Yes' : 'No').'</td>'; echo '<td>' . ($contestant['has_participated'] ? 'Yes' : 'No') . '</td>';
} }
echo '</tr>'; echo '</tr>';
}, },
[ [
'page_len' => 50, 'page_len' => 50,
'get_row_index' => '', 'get_row_index' => '',
'div_classes' => ['table-responsive', 'card', 'mb-3'], 'div_classes' => ['table-responsive', 'card', 'mb-3'],
'table_classes' => ['table', 'uoj-table', 'mb-0', 'text-center'], 'table_classes' => ['table', 'uoj-table', 'mb-0', 'text-center'],
] ]
); );
?> ?>
<?php <?php
if (isset($add_new_contestant_form)) { if (isset($add_new_contestant_form)) {
$add_new_contestant_form->printHTML(); $add_new_contestant_form->printHTML();
} }
if (isset($add_group_to_contest_form)) { if (isset($add_group_to_contest_form)) {
$add_group_to_contest_form->printHTML(); $add_group_to_contest_form->printHTML();
} }
if (isset($remove_user_from_contest_form)) { if (isset($remove_user_from_contest_form)) {
$remove_user_from_contest_form->printHTML(); $remove_user_from_contest_form->printHTML();
} }
if (isset($force_set_user_participated_form)) { if (isset($force_set_user_participated_form)) {
$force_set_user_participated_form->printHTML(); $force_set_user_participated_form->printHTML();
} }
?> ?>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,75 +1,57 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requirePHPLib('form'); requirePHPLib('form');
if (!validateUInt($_GET['id']) || !($contest = queryContest($_GET['id']))) { Auth::check() || redirectToLogin();
become404Page(); UOJContest::init(UOJRequest::get('id')) || UOJResponse::page404();
} UOJContest::cur()->userCanRegister(Auth::user(), ['ensure' => true]);
genMoreContestInfo($contest);
if (!Auth::check()) {
redirectToLogin();
} elseif (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) {
become403Page();
} elseif (hasRegistered($myUser, $contest)) {
if ($contest['cur_progress'] < CONTEST_IN_PROGRESS) {
redirectTo('/contests');
} elseif ($contest['cur_progress'] == CONTEST_IN_PROGRESS) {
redirectTo("/contest/{$contest['id']}/confirm");
} else {
redirectTo("/contest/{$contest['id']}");
}
} elseif ($contest['cur_progress'] == CONTEST_IN_PROGRESS && !(isset($contest['extra_config']['extra_registration']) ? $contest['extra_config']['extra_registration'] : '1')) {
becomeMsgPage('比赛已经开始,不允许报名。');
} elseif ($contest['cur_progress'] > CONTEST_IN_PROGRESS) {
redirectTo("/contest/{$contest['id']}");
}
$register_form = new UOJForm('register');
$register_form->handle = function() use ($myUser, $contest) {
DB::query("replace into contests_registrants (username, contest_id, has_participated) values ('{$myUser['username']}', {$contest['id']}, 0)");
updateContestPlayerNum($contest); $register_form = new UOJBs4Form('register');
}; $register_form->handle = function () {
$register_form->submit_button_config['class_str'] = 'btn btn-primary'; UOJContest::cur()->userRegister(Auth::user());
$register_form->submit_button_config['text'] = '我已阅读规则,确认报名比赛'; };
$register_form->submit_button_config['class_str'] = 'btn btn-primary';
if ($contest['cur_progress'] == CONTEST_IN_PROGRESS) { $register_form->submit_button_config['text'] = '我已阅读规则,确认报名比赛';
$register_form->succ_href = "/contest/{$contest['id']}/confirm";
} else { if (UOJContest::cur()->progress() == CONTEST_IN_PROGRESS) {
$register_form->succ_href = "/contests"; $register_form->succ_href = '/contest/' . UOJContest::info('id') . '/confirm';
} } else {
$register_form->succ_href = '/contests';
$register_form->runAtServer(); }
?>
<?php echoUOJPageHeader('报名 - ' . HTML::stripTags($contest['name'])) ?> $register_form->runAtServer();
?>
<?php echoUOJPageHeader('报名 - ' . UOJContest::info('name')) ?>
<div class="card mw-100 mx-auto" style="width:800px"> <div class="card mw-100 mx-auto" style="width:800px">
<div class="card-body"> <div class="card-body">
<h1 class="h2 card-title text-center mb-3">比赛规则</h1> <h1 class="card-title text-center mb-3">比赛规则</h1>
<p class="card-text">您即将报名比赛 <b><?= $contest['name'] ?></b>”,请在报名前仔细阅读以下比赛规则:</p> <p class="card-text">您即将报名比赛 <b><?= UOJContest::info('name') ?></b>”,请在报名前仔细阅读以下比赛规则:</p>
<ul> <ul>
<?php if ($contest['cur_progress'] == CONTEST_IN_PROGRESS): ?> <?php if (UOJContest::cur()->progress() == CONTEST_IN_PROGRESS) : ?>
<li class="text-danger">本场比赛正在进行中,将于 <b><?= $contest['end_time_str'] ?></b> 结束。</li> <li class="text-danger">本场比赛正在进行中,将于 <b><?= UOJContest::info('end_time_str') ?></b> 结束。</li>
<?php else: ?> <?php else : ?>
<li>本场比赛将于 <b><?= $contest['start_time_str'] ?></b> 开始,并于 <b><?= $contest['end_time_str'] ?></b> 结束。</li> <li>本场比赛将于 <b><?= UOJContest::info('start_time_str') ?></b> 开始,并于 <b><?= UOJContest::info('end_time_str') ?></b> 结束。</li>
<?php endif ?> <?php endif ?>
<li>比赛开始后点击 <b>确认参赛</b> 按钮才会被视为正式参赛,未正式参赛的选手不会显示在排行榜上。</li> <li>比赛开始后点击 <b>确认参赛</b> 按钮才会被视为正式参赛,未正式参赛的选手不会显示在排行榜上。</li>
<?php if (!isset($contest['extra_config']['contest_type']) || $contest['extra_config']['contest_type'] == 'OI'): ?> <?php if (UOJContest::cur()->basicRule() == 'OI') : ?>
<li>本场比赛为 OI 赛制。比赛中途可以提交代码,但 <b>只显示测样例的结果</b></li> <li>本场比赛为 OI 赛制。比赛中途可以提交代码,但 <b>只显示测样例的结果</b></li>
<?php elseif ($contest['extra_config']['contest_type'] == 'IOI'): ?> <?php elseif (UOJContest::cur()->basicRule() == 'IOI') : ?>
<li>本场比赛为 IOI 赛制。比赛时的提交会测试题目的全部数据,但无法查看数据点详情。</li> <li>本场比赛为 IOI 赛制。比赛时的提交会测试题目的全部数据,但无法查看数据点详情。</li>
<?php endif ?> <?php elseif (UOJContest::cur()->basicRule() == 'ACM') : ?>
<li>若选手在比赛中多次提交了同一题,则最后按照 <b>最后一次不是 Compile Error 的提交</b> 计算排行。</li> <li>本场比赛为 ACM 赛制。</li>
<li>比赛结束后会进行最终测试,最终测试后的排名为最终排名。</li> <?php endif ?>
<li>比赛排名按分数为第一关键字,完成题目的总时间为第二关键字。完成题目的总时间等于完成每道题所花时间之和(无视掉爆零的题目)。</li> <li>若选手在比赛中多次提交了同一题,则最后按照 <b>最后一次不是 Compile Error 的提交</b> 计算排行。</li>
<li>请遵守比赛规则,一位选手在一场比赛内不得报名多个账号,选手之间不能交流或者抄袭代码,如果被检测到将以 0 分处理或者封禁。</li> <li>比赛结束后会进行最终测试,最终测试后的排名为最终排名。</li>
</ul> <li>比赛排名按分数为第一关键字,完成题目的总时间为第二关键字。完成题目的总时间等于完成每道题所花时间之和(无视掉爆零的题目)。</li>
<li>请遵守比赛规则,一位选手在一场比赛内不得报名多个账号,选手之间不能交流或者抄袭代码,如果被检测到将以 0 分处理或者封禁。</li>
</ul>
<?php $register_form->printHTML() ?> <?php $register_form->printHTML() ?>
</div> </div>
</div> </div>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,108 +1,110 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requirePHPLib('form'); requirePHPLib('form');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin();
}
$upcoming_contest_name = null; $upcoming_contest_name = null;
$upcoming_contest_href = null; $upcoming_contest_href = null;
$rest_second = 1000000; $rest_second = 1000000;
function echoContest($contest) { function echoContest($info) {
global $myUser, $upcoming_contest_name, $upcoming_contest_href, $rest_second; global $upcoming_contest_name, $upcoming_contest_href, $rest_second;
$contest_name_link = <<<EOD $contest = new UOJContest($info);
<a class="text-decoration-none" href="/contest/{$contest['id']}">{$contest['name']}</a>
EOD; $contest_name_link = '<a class="text-decoration-none" href="/contest/' . $contest->info['id'] . '">' . $contest->info['name'] . '</a>';
genMoreContestInfo($contest);
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) { if ($contest->progress() == CONTEST_NOT_STARTED) {
$cur_rest_second = $contest['start_time']->getTimestamp() - UOJTime::$time_now->getTimestamp(); $cur_rest_second = $contest->info['start_time']->getTimestamp() - UOJTime::$time_now->getTimestamp();
if ($cur_rest_second < $rest_second) { if ($cur_rest_second < $rest_second) {
$upcoming_contest_name = $contest['name']; $upcoming_contest_name = $contest->info['name'];
$upcoming_contest_href = "/contest/{$contest['id']}"; $upcoming_contest_href = '/contest/' . $contest->info['id'];
$rest_second = $cur_rest_second; $rest_second = $cur_rest_second;
} }
if ($myUser != null && hasRegistered($myUser, $contest)) { if ($contest->userHasRegistered(Auth::user())) {
$contest_name_link .= '<sup><a class="text-decoration-none" style="color:green">'.UOJLocale::get('contests::registered').'</a></sup>'; $contest_name_link .= '<sup><a class="text-decoration-none" style="color:green">' . UOJLocale::get('contests::registered') . '</a></sup>';
} else { } else {
$contest_name_link .= '<sup><a class="text-decoration-none" style="color:red" href="/contest/'.$contest['id'].'/register">'.UOJLocale::get('contests::register').'</a></sup>'; $contest_name_link .= '<sup><a class="text-decoration-none" style="color:red" href="/contest/' . $contest->info['id'] . '/register">' . UOJLocale::get('contests::register') . '</a></sup>';
} }
} elseif ($contest['cur_progress'] == CONTEST_IN_PROGRESS) { } elseif ($contest->progress() == CONTEST_IN_PROGRESS) {
if (hasRegistered($myUser, $contest)) { if ($contest->allowExtraRegistration() && !$contest->userHasRegistered(Auth::user())) {
$contest_name_link .= '<sup><a class="text-decoration-none" style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::in progress').'</a></sup>'; $contest_name_link .= '<sup><a class="text-decoration-none" style="color:red" href="/contest/' . $contest->info['id'] . '/register">' . UOJLocale::get('contests::register') . ' (' . UOJLocale::get('contests::in progress') . ')' . '</a></sup>';
} else { } else {
$contest_name_link .= '<sup><a class="text-decoration-none" style="color:blue" href="/contest/'.$contest['id'].'/register">'.UOJLocale::get('contests::in progress').'</a></sup>'; $contest_name_link .= '<sup><a class="text-decoration-none" style="color:blue" href="/contest/' . $contest->info['id'] . '">' . UOJLocale::get('contests::in progress') . '</a></sup>';
} }
} elseif ($contest['cur_progress'] == CONTEST_PENDING_FINAL_TEST) { } elseif ($contest->progress() == CONTEST_FINISHED) {
$contest_name_link .= '<sup><a class="text-decoration-none" style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::pending final test').'</a></sup>'; $contest_name_link .= '<sup><a class="text-decoration-none" style="color:grey" href="/contest/' . $contest->info['id'] . '/standings">' . UOJLocale::get('contests::ended') . '</a></sup>';
} elseif ($contest['cur_progress'] == CONTEST_TESTING) { } else {
$contest_name_link .= '<sup><a class="text-decoration-none" style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::final testing').'</a></sup>'; if ($contest->basicRule() == 'OI') {
} elseif ($contest['cur_progress'] == CONTEST_FINISHED) { if ($contest->progress() == CONTEST_PENDING_FINAL_TEST) {
$contest_name_link .= '<sup><a class="text-decoration-none" style="color:grey" href="/contest/'.$contest['id'].'/standings">'.UOJLocale::get('contests::ended').'</a></sup>'; $contest_name_link .= '<sup><a style="color:blue" href="/contest/' . $contest->info['id'] . '">' . UOJLocale::get('contests::pending final test') . '</a></sup>';
} elseif ($contest->progress() == CONTEST_TESTING) {
$contest_name_link .= '<sup><a style="color:blue" href="/contest/' . $contest->info['id'] . '">' . UOJLocale::get('contests::final testing') . '</a></sup>';
}
} elseif ($contest->basicRule() == 'ACM' || $contest->basicRule() == 'IOI') {
$contest_name_link .= '<sup><a style="color:blue" href="/contest/' . $contest->info['id'] . '">' . UOJLocale::get('contests::official results to be announced') . '</a></sup>';
} }
$last_hour = round($contest['last_min'] / 60, 2);
$click_zan_block = getClickZanBlock('C', $contest['id'], $contest['zan']);
echo '<tr>';
echo '<td>', $contest_name_link, '</td>';
echo '<td>', '<a class="text-decoration-none" href="'.HTML::timeanddate_url($contest['start_time'], array('duration' => $contest['last_min'])).'">'.$contest['start_time_str'].'</a>', '</td>';
echo '<td>', UOJLocale::get('hours', $last_hour), '</td>';
echo '<td>', '<a class="text-decoration-none" href="/contest/'.$contest['id'].'/registrants">', '<i class="bi bi-person-fill"></i>', ' &times;'.$contest['player_num'].'</a>', '</td>';
echo '<td>', '<div class="text-left">'.$click_zan_block.'</div>', '</td>';
echo '</tr>';
} }
?>
$last_hour = round($contest->info['last_min'] / 60, 2);
echo '<tr>';
echo '<td>', $contest_name_link, '</td>';
echo '<td>', '<a class="text-decoration-none" href="' . HTML::timeanddate_url($contest->info['start_time'], ['duration' => $contest->info['last_min']]) . '">' . $contest->info['start_time_str'] . '</a>', '</td>';
echo '<td>', UOJLocale::get('hours', $last_hour), '</td>';
echo '<td>', '<a class="text-decoration-none" href="/contest/' . $contest->info['id'] . '/registrants">', '<i class="bi bi-person-fill"></i>', ' &times;' . $contest->info['player_num'] . '</a>', '</td>';
echo '<td>', '<div class="text-left">' . ClickZans::getBlock('C', $contest->info['id'], $contest->info['zan']) . '</div>', '</td>';
echo '</tr>';
}
?>
<?php echoUOJPageHeader(UOJLocale::get('contests')) ?> <?php echoUOJPageHeader(UOJLocale::get('contests')) ?>
<!-- title container --> <!-- title container -->
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<h1 class="h2"> <h1>
<?= UOJLocale::get('contests') ?> <?= UOJLocale::get('contests') ?>
</h1> </h1>
<?php if (isSuperUser($myUser)): ?> <?php if (isSuperUser($myUser)) : ?>
<div class="text-end"> <div class="text-end">
<a href="/contest/new" class="btn btn-primary"><?= UOJLocale::get('contests::add new contest') ?></a> <a href="/contest/new" class="btn btn-primary"><?= UOJLocale::get('contests::add new contest') ?></a>
</div> </div>
<?php endif ?> <?php endif ?>
</div> </div>
<!-- end title container --> <!-- end title container -->
<h2 class="h4"> <h2>
<?= UOJLocale::get('contests::current or upcoming contests') ?> <?= UOJLocale::get('contests::current or upcoming contests') ?>
</h2> </h2>
<?php <?php
$table_header = ''; $table_header = '';
$table_header .= '<tr>'; $table_header .= '<tr>';
$table_header .= '<th>'.UOJLocale::get('contests::contest name').'</th>'; $table_header .= '<th>' . UOJLocale::get('contests::contest name') . '</th>';
$table_header .= '<th style="width:15em;">'.UOJLocale::get('contests::start time').'</th>'; $table_header .= '<th style="width:15em;">' . UOJLocale::get('contests::start time') . '</th>';
$table_header .= '<th style="width:100px;">'.UOJLocale::get('contests::duration').'</th>'; $table_header .= '<th style="width:100px;">' . UOJLocale::get('contests::duration') . '</th>';
$table_header .= '<th style="width:100px;">'.UOJLocale::get('contests::the number of registrants').'</th>'; $table_header .= '<th style="width:100px;">' . UOJLocale::get('contests::the number of registrants') . '</th>';
$table_header .= '<th style="width:180px;">'.UOJLocale::get('appraisal').'</th>'; $table_header .= '<th style="width:180px;">' . UOJLocale::get('appraisal') . '</th>';
$table_header .= '</tr>'; $table_header .= '</tr>';
$table_config = [ $table_config = [
'page_len' => 40, 'page_len' => 40,
'div_classes' => ['card', 'mb-3'], 'div_classes' => ['card', 'mb-3'],
'table_classes' => ['table', 'uoj-table', 'mb-0', 'text-center'], 'table_classes' => ['table', 'uoj-table', 'mb-0', 'text-center'],
]; ];
echoLongTable( echoLongTable(
['*'], ['*'],
'contests', 'contests',
"status != 'finished'", [["status", "!=", 'finished']],
'order by start_time asc, id asc', 'order by start_time asc, id asc',
$table_header, $table_header,
echoContest, 'echoContest',
$table_config $table_config
); );
if ($rest_second <= 86400) { if ($rest_second <= 86400) {
$notification = json_encode($upcoming_contest_name . " 已经开始了。是否要跳转到比赛页面?"); $notification = json_encode($upcoming_contest_name . " 已经开始了。是否要跳转到比赛页面?");
echo <<<EOD echo <<<EOD
<div class="text-center bot-buffer-lg"> <div class="text-center bot-buffer-lg">
<div class="text-secondary">$upcoming_contest_name 倒计时</div> <div class="text-secondary">$upcoming_contest_name 倒计时</div>
<div id="contest-countdown"></div> <div id="contest-countdown"></div>
@ -115,21 +117,23 @@ $('#contest-countdown').countdown($rest_second, function() {
</script> </script>
</div> </div>
EOD; EOD;
} }
?> ?>
<h2 class="h4"> <h2>
<?= UOJLocale::get('contests::ended contests') ?> <?= UOJLocale::get('contests::ended contests') ?>
</h2> </h2>
<?php <?php
echoLongTable( echoLongTable(
['*'], ['*'],
'contests', 'contests',
"status = 'finished'", ['status' => 'finished'],
'order by start_time desc, id desc', 'order by start_time desc, id desc',
$table_header, $table_header,
echoContest, 'echoContest',
$table_config $table_config
); );
?> ?>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,140 +1,61 @@
<?php <?php
requirePHPLib('judger'); requirePHPLib('judger');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { $auth = false;
redirectToLogin(); if (UOJRequest::get('auth') === 'judger') {
} authenticateJudger() || UOJResponse::page403();
$auth = true;
} else {
Auth::check() || redirectToLogin();
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login'] && $_GET['type'] != 'attachment') { switch (UOJRequest::get('type')) {
become403Page(); case 'attachment':
} UOJProblem::init(UOJRequest::get('id')) || UOJResponse::page404();
if (!$auth) {
UOJProblem::cur()->userCanDownloadAttachments(Auth::user()) || UOJResponse::page404();
}
switch ($_GET['type']) { $file_name = UOJProblem::cur()->getDataFolderPath() . '/download.zip';
case 'attachment': $download_name = 'problem_' . UOJProblem::info('id') . '_attachment.zip';
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {
become404Page();
}
$visible = isProblemVisibleToUser($problem, $myUser);
if (!$visible && $myUser != null) {
$result = DB::query("select contest_id from contests_problems where problem_id = {$_GET['id']}");
while (list($contest_id) = DB::fetch($result, MYSQLI_NUM)) {
$contest = queryContest($contest_id);
genMoreContestInfo($contest);
if ($contest['cur_progress'] != CONTEST_NOT_STARTED && hasRegistered($myUser, $contest) && queryContestProblemRank($contest, $problem)) {
$visible = true;
}
}
}
if (!$visible) {
become404Page();
}
$id = $_GET['id']; break;
$file_name = "/var/uoj_data/$id/download.zip";
$download_name = "problem_{$id}_attachment.zip";
break;
case 'problem': case 'problem':
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) { UOJProblem::init(UOJRequest::get('id')) || UOJResponse::page404();
become404Page();
}
if (!hasProblemPermission($myUser, $problem)) { if (!$auth) {
become403Page(); UOJProblem::cur()->userCanDownloadTestData(Auth::user()) || UOJResponse::page404();
} }
$id = $_GET['id']; $file_name = UOJProblem::cur()->getDataZipPath();
$file_name = "/var/uoj_data/$id.zip"; $download_name = 'problem_' . UOJProblem::info('id') . '.zip';
$download_name = "problem_$id.zip";
break; break;
case 'testcase': case 'submission':
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) { if (!$auth) {
become404Page(); isSuperUser(Auth::user()) || UOJResponse::page404();
} }
$file_name = UOJContext::storagePath() . "/submission/{$_GET['id']}/{$_GET['rand_str_id']}";
$download_name = "submission.zip";
break;
if (!hasProblemPermission($myUser, $problem)) { case 'tmp':
become404Page(); if (!$auth) {
} isSuperUser(Auth::user()) || UOJResponse::page404();
}
$file_name = UOJContext::storagePath() . "/tmp/{$_GET['rand_str_id']}";
$download_name = "tmp";
break;
$id = $_GET['id']; case 'testlib.h':
$problem_conf = getUOJConf("/var/uoj_data/$id/problem.conf"); $file_name = UOJLocalRun::$judger_include_path . '/testlib.h';
$download_name = 'testlib.h';
break;
if ($problem_conf == -1 || $problem_conf == -2) { default:
become404Page(); UOJResponse::page404();
} }
if (!validateUInt($_GET['testcase_id'])) { UOJResponse::xsendfile($file_name, ['attachment' => $download_name]);
become404Page();
}
$testcase_id = $_GET['testcase_id'];
$testcase_group = isset($_GET['testcase_group']) && $_GET['testcase_group'] == 'extra' ? 'extra' : 'normal';
if ($testcase_group == 'extra') {
$n_ex_tests = getUOJConfVal($problem_conf, 'n_ex_tests', 0);
if ($testcase_id < 1 || $testcase_id > $n_ex_tests) {
become404Page();
}
switch ($_GET['testcase_type']) {
case 'input':
$file_name = "/var/uoj_data/$id/" . getUOJProblemExtraInputFileName($problem_conf, $testcase_id);
$download_name = getUOJProblemExtraInputFileName($problem_conf, $testcase_id);
break;
case 'output':
$file_name = "/var/uoj_data/$id/" . getUOJProblemExtraOutputFileName($problem_conf, $testcase_id);
$download_name = getUOJProblemExtraOutputFileName($problem_conf, $testcase_id);
break;
default:
become404Page();
}
} else {
$n_tests = getUOJConfVal($problem_conf, 'n_tests', 10);
if ($testcase_id < 1 || $testcase_id > $n_tests) {
become404Page();
}
switch ($_GET['testcase_type']) {
case 'input':
$file_name = "/var/uoj_data/$id/" . getUOJProblemInputFileName($problem_conf, $testcase_id);
$download_name = getUOJProblemInputFileName($problem_conf, $testcase_id);
break;
case 'output':
$file_name = "/var/uoj_data/$id/" . getUOJProblemOutputFileName($problem_conf, $testcase_id);
$download_name = getUOJProblemOutputFileName($problem_conf, $testcase_id);
break;
default:
become404Page();
}
}
break;
case 'testlib.h':
$file_name = "/opt/uoj/judger/uoj_judger/include/testlib.h";
$download_name = "testlib.h";
break;
default:
become404Page();
}
$finfo = finfo_open(FILEINFO_MIME);
$mimetype = finfo_file($finfo, $file_name);
if ($mimetype === false) {
become404Page();
}
finfo_close($finfo);
header("X-Sendfile: $file_name");
header("Content-type: $mimetype");
header("Content-Disposition: attachment; filename=$download_name");
?>

View File

@ -1,122 +1,120 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requireLib('hljs'); requireLib('hljs');
requireLib('mathjax'); requireLib('mathjax');
?> ?>
<?php echoUOJPageHeader(UOJLocale::get('help')) ?> <?php echoUOJPageHeader(UOJLocale::get('help')) ?>
<div class="row"> <div class="row">
<!-- left col --> <!-- left col -->
<div class="col-lg-9"> <div class="col-lg-9">
<div class="card card-default">
<article class="card-body">
<h1 class="h2 card-title mb-3">常见问题及其解答 (FAQ)</h1>
<div class="card card-default"> <h5 class="mt-4"><?= UOJConfig::$data['profile']['oj-name-short'] ?> 是什么</h5>
<article class="card-body"> <p class="card-text">
<h1 class="h3 card-title mb-3">常见问题及其解答 (FAQ)</h1> <a href="https://sjzezoj.com/blogs/1">https://sjzezoj.com/blogs/1</a>
</p>
<h5 class="mt-4"><?= UOJConfig::$data['profile']['oj-name-short'] ?> 是什么</h5> <h5 class="mt-4">测评环境</h5>
<p class="card-text"> <p class="card-text">评测机的系统版本是 Ubuntu Server 22.04 LTS。</p>
<a href="https://sjzezoj.com/blogs/1">https://sjzezoj.com/blogs/1</a> <div class="table-responsive">
</p> <table class="table table-bordered text-center align-middle">
<thead>
<th>语言</th>
<th>版本</th>
<th>编译命令</th>
</thead>
<tbody>
<tr>
<td>C</td>
<td>gcc 11.2.0</td>
<td><code>gcc -o code code.c -lm -O2 -DONLINE_JUDGE</code></td>
</tr>
<tr>
<td>C++</td>
<td>g++ 11.2.0</td>
<td><code>g++ -o code code.cpp -lm -O2 -DONLINE_JUDGE</code>(语言版本默认为 C++14,如选择其他版本还会添加 <code>-std=</code> 参数)</td>
</tr>
<tr>
<td>Pascal</td>
<td>fpc 3.2.2</td>
<td><code>fpc code.pas -O2</code></td>
</tr>
<tr>
<td>Python 2</td>
<td>Python 2.7.18</td>
<td rowspan="2">预先编译为优化过的字节码 <code>.pyo</code> 文件</td>
</tr>
<tr>
<td>Python 3</td>
<td>Python 3.10.6</td>
</tr>
<tr>
<td>Java 8</td>
<td>OpenJDK 1.8.0_342</td>
<td rowspan="3"><code>javac code.java</code></td>
</tr>
<tr>
<td>Java 11</td>
<td>OpenJDK 11.0.16</td>
</tr>
<tr>
<td>Java 17</td>
<td>OpenJDK 17.0.4</td>
</tr>
</tbody>
</table>
</div>
<p class="card-text">以上信息仅供参考,实际评测环境可能会有变动。</p>
<h5 class="mt-4">测评环境</h5> <h5 class="mt-4">如何上传头像</h5>
<p class="card-text">评测机的系统版本是 Ubuntu Server 22.04 LTS。</p> <p class="card-text">
<div class="table-responsive"> <?= UOJConfig::$data['profile']['oj-name-short'] ?> 不提供头像存储服务。每到一个网站都要上传一个头像挺烦的对不对?我们支持 Gravatar请使用 Gravatar 吧Gravatar 是一个全球的头像存储服务,你的头像将会与你的电子邮箱绑定。在各大网站比如各种 Wordpress 还有各种 OJ 比如 Vijos、Contest Hunter 上,只要你电子邮箱填对了,那么你的头像也就立即能显示了!
<table class="table table-bordered text-center align-middle"> </p>
<thead> <p class="card-text">
<th>语言</th> 快使用 Gravatar Gravatar 地址:<a href="https://cn.gravatar.com" target="_blank">https://cn.gravatar.com</a>。进去后注册个帐号然后与邮箱绑定并上传头像,就 OK 啦!
<th>版本</th> </p>
<th>编译命令</th> <p class="card-text">
</thead> 上不去 Gravatar没关系我们现在也支持 QQ 头像了!你只需要前往 “更改个人信息” 页面填写自己的 QQ 号,并将 “头像来源” 选为 “QQ” 就可以让你的 QQ 头像显示在 S2OJ 上啦!
<tbody> </p>
<tr>
<td>C</td> <h5 class="mt-4">递归 10<sup>7</sup> 层怎么没爆栈啊</h5>
<td>gcc 11.2.0</td> <p class="card-text">
<td><code>gcc -o code code.c -lm -O2 -DONLINE_JUDGE</code></td> 没错就是这样!除非是特殊情况,<?= UOJConfig::$data['profile']['oj-name-short'] ?> 测评程序时的栈大小与该题的空间限制是相等的!
</tr> </p>
<tr>
<td>C++</td> <h5 class="mt-4">联系方式</h5>
<td>g++ 11.2.0</td> <p class="card-text">
<td><code>g++ -o code code.cpp -lm -O2 -DONLINE_JUDGE</code>(语言版本默认为 C++14,如选择其他版本还会添加 <code>-std=</code> 参数)</td> 题目相关问题请联系各校区的竞赛教练以及题目管理员。
</tr> </p>
<tr> <p class="card-text">
<td>Pascal</td> 系统相关问题请邮件联系 <a href="https://sjzezoj.com/user/baoshuo" class="uoj-username">baoshuo</a><a href="mailto:i@baoshuo.ren">i@baoshuo.ren</a>
<td>fpc 3.2.2</td> <a href="https://sjzezoj.com/user/nekko" class="uoj-username">nekko</a><a href="mailto:1139855151@qq.com">1139855151@qq.com</a>)。
<td><code>fpc code.pas -O2</code></td> </p>
</tr>
<tr> <h5 class="mt-4">开源项目</h5>
<td>Python 2</td> <p class="card-text">
<td>Python 2.7.18</td> <?= UOJConfig::$data['profile']['oj-name-short'] ?> 的源代码存放于
<td rowspan="2">预先编译为优化过的字节码 <code>.pyo</code> 文件</td> <a href="https://github.com/renbaoshuo/S2OJ" target="_blank">https://github.com/renbaoshuo/S2OJ</a>
</tr> 如果你网不太好,打不开 GitHub 的话,也可以点击 <a href="https://git.m.ac/baoshuo/S2OJ" target="_blank">https://git.m.ac/baoshuo/S2OJ</a> 查看哦!这两个仓库的内容是一模一样的。
<tr> </p>
<td>Python 3</td>
<td>Python 3.10.6</td> <h5 class="mt-4">用户手册</h5>
</tr> <p class="card-text">
<tr> 请移步 <a href="https://s2oj.github.io/">S2OJ 使用文档</a>
<td>Java 8</td> </p>
<td>OpenJDK 1.8.0_342</td> </article>
<td rowspan="3"><code>javac code.java</code></td> </div>
</tr> <!-- end left col -->
<tr>
<td>Java 11</td>
<td>OpenJDK 11.0.16</td>
</tr>
<tr>
<td>Java 17</td>
<td>OpenJDK 17.0.4</td>
</tr>
</tbody>
</table>
</div> </div>
<p class="card-text">以上信息仅供参考,实际评测环境可能会有变动。</p>
<h5 class="mt-4">如何上传头像</h5> <!-- right col -->
<p class="card-text"> <aside class="col-lg-3 mt-3 mt-lg-0">
<?= UOJConfig::$data['profile']['oj-name-short'] ?> 不提供头像存储服务。每到一个网站都要上传一个头像挺烦的对不对?我们支持 Gravatar请使用 Gravatar 吧Gravatar 是一个全球的头像存储服务,你的头像将会与你的电子邮箱绑定。在各大网站比如各种 Wordpress 还有各种 OJ 比如 Vijos、Contest Hunter 上,只要你电子邮箱填对了,那么你的头像也就立即能显示了! <?php uojIncludeView('sidebar') ?>
</p> </aside>
<p class="card-text">
快使用 Gravatar Gravatar 地址:<a href="https://cn.gravatar.com" target="_blank">https://cn.gravatar.com</a>。进去后注册个帐号然后与邮箱绑定并上传头像,就 OK 啦!
</p>
<p class="card-text">
上不去 Gravatar没关系我们现在也支持 QQ 头像了!你只需要前往 “更改个人信息” 页面填写自己的 QQ 号,并将 “头像来源” 选为 “QQ” 就可以让你的 QQ 头像显示在 S2OJ 上啦!
</p>
<h5 class="mt-4">递归 10<sup>7</sup> 层怎么没爆栈啊</h5>
<p class="card-text">
没错就是这样!除非是特殊情况,<?= UOJConfig::$data['profile']['oj-name-short'] ?> 测评程序时的栈大小与该题的空间限制是相等的!
</p>
<h5 class="mt-4">联系方式</h5>
<p class="card-text">
题目相关问题请联系各校区的竞赛教练以及题目管理员。
</p>
<p class="card-text">
系统相关问题请邮件联系 <a href="https://sjzezoj.com/user/baoshuo" class="uoj-username">baoshuo</a><a href="mailto:i@baoshuo.ren">i@baoshuo.ren</a>
<a href="https://sjzezoj.com/user/nekko" class="uoj-username">nekko</a><a href="mailto:1139855151@qq.com">1139855151@qq.com</a>)。
</p>
<h5 class="mt-4">开源项目</h5>
<p class="card-text">
<?= UOJConfig::$data['profile']['oj-name-short'] ?> 的源代码存放于
<a href="https://github.com/renbaoshuo/S2OJ" target="_blank">https://github.com/renbaoshuo/S2OJ</a>
如果你网不太好,打不开 GitHub 的话,也可以点击 <a href="https://git.m.ac/baoshuo/S2OJ" target="_blank">https://git.m.ac/baoshuo/S2OJ</a> 查看哦!这两个仓库的内容是一模一样的。
</p>
<h5 class="mt-4">用户手册</h5>
<p class="card-text">
请移步 <a href="https://s2oj.github.io/">S2OJ 使用文档</a>
</p>
</article>
</div>
<!-- end left col -->
</div>
<!-- right col -->
<aside class="col-lg-3 mt-3 mt-lg-0">
<?php uojIncludeView('sidebar', array()) ?>
</aside>
</div> </div>

View File

@ -1,7 +1,7 @@
<?php <?php
requirePHPLib('form'); requirePHPLib('form');
$forgot_form = new UOJForm('forgot'); $forgot_form = new UOJBs4Form('forgot');
$forgot_form->addInput('username', 'text', '用户名', '', $forgot_form->addInput('username', 'text', '用户名', '',
function($username, &$vdata) { function($username, &$vdata) {
if (!validateUsername($username)) { if (!validateUsername($username)) {

View File

@ -4,14 +4,10 @@
requirePHPLib('judger'); requirePHPLib('judger');
requirePHPLib('data'); requirePHPLib('data');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { if (!Auth::check()) {
redirectToLogin(); redirectToLogin();
} }
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) {
become403Page();
}
$group_id = $_GET['id']; $group_id = $_GET['id'];
if (!validateUInt($group_id) || !($group = queryGroup($group_id))) { if (!validateUInt($group_id) || !($group = queryGroup($group_id))) {
become404Page(); become404Page();
@ -30,7 +26,7 @@
<!-- title --> <!-- title -->
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<h1 class="h2"> <h1>
<?php if ($group['is_hidden']): ?> <?php if ($group['is_hidden']): ?>
<span class="fs-5 text-danger">[隐藏]</span> <span class="fs-5 text-danger">[隐藏]</span>
<?php endif ?> <?php endif ?>
@ -51,7 +47,7 @@
<!-- main content --> <!-- main content -->
<div class="card mb-3"> <div class="card mb-3">
<div class="card-body"> <div class="card-body">
<h2 class="h4"> <h2 class="h3">
<?= UOJLocale::get('group announcement') ?> <?= UOJLocale::get('group announcement') ?>
</h2> </h2>
<?php if ($group['announcement']): ?> <?php if ($group['announcement']): ?>
@ -68,7 +64,7 @@
<div class="card mb-3"> <div class="card mb-3">
<div class="card-body"> <div class="card-body">
<h2 class="card-title h4"> <h2 class="card-title h3">
<?= UOJLocale::get('news') ?> <?= UOJLocale::get('news') ?>
</h5> </h5>
<ul class="mb-0"> <ul class="mb-0">
@ -92,7 +88,7 @@
<div class="card card-default mb-3"> <div class="card card-default mb-3">
<div class="card-body"> <div class="card-body">
<h2 class="card-title h4"> <h2 class="card-title h3">
<?= UOJLocale::get('assignments') ?> <?= UOJLocale::get('assignments') ?>
</h5> </h5>
<?php <?php
@ -139,7 +135,7 @@ EOD,
<div class="card card-default mb-3"> <div class="card card-default mb-3">
<div class="card-body"> <div class="card-body">
<h2 class="card-title h4"> <h2 class="card-title h3">
<?= UOJLocale::get('top solver') ?> <?= UOJLocale::get('top solver') ?>
</h5> </h5>
<?php echoRanklist([ <?php echoRanklist([

View File

@ -1,12 +1,8 @@
<?php <?php
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { if (!Auth::check()) {
redirectToLogin(); redirectToLogin();
} }
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) {
become403Page();
}
requirePHPLib('form'); requirePHPLib('form');
requireLib('bootstrap5'); requireLib('bootstrap5');
@ -40,7 +36,7 @@
<!-- left col --> <!-- left col -->
<div class="col-lg-9"> <div class="col-lg-9">
<h1 class="h2"> <h1>
<small class="fs-4">作业:</small><?= $list['title'] ?> <small class="fs-4">作业:</small><?= $list['title'] ?>
</h1> </h1>
<ul class="mt-3"> <ul class="mt-3">

View File

@ -43,7 +43,7 @@
} }
if ($cur_tab == 'profile') { if ($cur_tab == 'profile') {
$update_profile_form = new UOJForm('update_profile'); $update_profile_form = new UOJBs4Form('update_profile');
$update_profile_form->addVInput('name', 'text', '名称', $group['title'], $update_profile_form->addVInput('name', 'text', '名称', $group['title'],
function($title, &$vdata) { function($title, &$vdata) {
if ($title == '') { if ($title == '') {
@ -107,7 +107,7 @@ function(res) {
$(window).scrollTop(0); $(window).scrollTop(0);
} }
EOD); EOD);
$update_profile_form->submit_button_config['margin_class'] = 'mt-3'; $update_profile_form->submit_button_config['class_str'] = 'btn btn-secondary mt-3';
$update_profile_form->submit_button_config['text'] = '更新'; $update_profile_form->submit_button_config['text'] = '更新';
$update_profile_form->runAtServer(); $update_profile_form->runAtServer();
} elseif ($cur_tab == 'assignments') { } elseif ($cur_tab == 'assignments') {
@ -127,7 +127,7 @@ EOD);
dieWithAlert('移除成功!'); dieWithAlert('移除成功!');
} }
$add_new_assignment_form = new UOJForm('add_new_assignment'); $add_new_assignment_form = new UOJBs4Form('add_new_assignment');
$add_new_assignment_form->addVInput('new_assignment_list_id', 'text', '题单 ID', '', $add_new_assignment_form->addVInput('new_assignment_list_id', 'text', '题单 ID', '',
function ($list_id, &$vdata) use ($group) { function ($list_id, &$vdata) use ($group) {
if (!validateUInt($list_id)) { if (!validateUInt($list_id)) {
@ -177,7 +177,7 @@ EOD);
'message' => '题单 #' . $vdata['list_id'] . ' 已经被添加到作业列表中,结束时间为 ' . $vdata['end_time']->format('Y-m-d H:i:s') . '。' 'message' => '题单 #' . $vdata['list_id'] . ' 已经被添加到作业列表中,结束时间为 ' . $vdata['end_time']->format('Y-m-d H:i:s') . '。'
]); ]);
}; };
$add_new_assignment_form->submit_button_config['margin_class'] = 'mt-3'; $add_new_assignment_form->submit_button_config['class_str'] = 'btn btn-secondary mt-3';
$add_new_assignment_form->submit_button_config['text'] = '添加'; $add_new_assignment_form->submit_button_config['text'] = '添加';
$add_new_assignment_form->setAjaxSubmit(<<<EOD $add_new_assignment_form->setAjaxSubmit(<<<EOD
function(res) { function(res) {
@ -223,7 +223,7 @@ EOD);
dieWithAlert('移除成功!'); dieWithAlert('移除成功!');
} }
$add_new_user_form = new UOJForm('add_new_user'); $add_new_user_form = new UOJBs4Form('add_new_user');
$add_new_user_form->addVInput('new_username', 'text', '用户名', '', $add_new_user_form->addVInput('new_username', 'text', '用户名', '',
function ($username, &$vdata) { function ($username, &$vdata) {
global $group_id; global $group_id;
@ -246,7 +246,7 @@ EOD);
}, },
null null
); );
$add_new_user_form->submit_button_config['margin_class'] = 'mt-3'; $add_new_user_form->submit_button_config['class_str'] = 'btn btn-secondary mt-3';
$add_new_user_form->submit_button_config['text'] = '添加'; $add_new_user_form->submit_button_config['text'] = '添加';
$add_new_user_form->handle = function(&$vdata) use ($group) { $add_new_user_form->handle = function(&$vdata) use ($group) {
DB::insert("insert into groups_users (group_id, username) values ({$group['id']}, '{$vdata['username']}')"); DB::insert("insert into groups_users (group_id, username) values ({$group['id']}, '{$vdata['username']}')");
@ -277,7 +277,7 @@ EOD);
?> ?>
<?php echoUOJPageHeader('管理 - ' . $group['title']); ?> <?php echoUOJPageHeader('管理 - ' . $group['title']); ?>
<h1 class="h2 d-block d-md-inline-block"> <h1 class="d-block d-md-inline-block">
<?= $group['title'] ?> <?= $group['title'] ?>
<small class="fs-5">(ID: #<?= $group['id'] ?>)</small> <small class="fs-5">(ID: #<?= $group['id'] ?>)</small>
管理 管理

View File

@ -1,57 +1,50 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requirePHPLib('form'); requirePHPLib('form');
requirePHPLib('judger'); requirePHPLib('judger');
requirePHPLib('data'); requirePHPLib('data');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { if (!Auth::check()) {
redirectToLogin(); redirectToLogin();
} }
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { if (isSuperUser($myUser)) {
become403Page(); $new_group_form = new UOJBs4Form('new_group');
} $new_group_form->handle = function () {
DB::query("insert into `groups` (title, is_hidden) values ('新小组', 1)");
if (isSuperUser($myUser)) { };
$new_group_form = new UOJForm('new_group'); $new_group_form->submit_button_config['align'] = 'right';
$new_group_form->handle = function() { $new_group_form->submit_button_config['class_str'] = 'btn btn-primary';
DB::query("insert into `groups` (title, is_hidden) values ('新小组', 1)"); $new_group_form->submit_button_config['text'] = UOJLocale::get('add new group');
}; $new_group_form->submit_button_config['smart_confirm'] = '';
$new_group_form->submit_button_config['align'] = 'right'; $new_group_form->runAtServer();
$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();
}
?>
<?php echoUOJPageHeader(UOJLocale::get('groups')) ?> <?php echoUOJPageHeader(UOJLocale::get('groups')) ?>
<div class="row"> <div class="row">
<!-- left col -->
<div class="col-lg-9">
<!-- title container -->
<div class="d-flex justify-content-between">
<h1>
<?= UOJLocale::get('groups') ?>
</h1>
<!-- left col --> <?php if (isset($new_group_form)) : ?>
<div class="col-lg-9"> <div class="text-end mb-2">
<?php $new_group_form->printHTML(); ?>
</div>
<?php endif ?>
<!-- title container --> </div>
<div class="d-flex justify-content-between"> <!-- end title container -->
<h1 class="h2"> <?php
<?= UOJLocale::get('groups') ?> $groups_caption = UOJLocale::get('groups');
</h1> $users_caption = UOJLocale::get('users count');
$header = <<<EOD
<?php if (isset($new_group_form)): ?>
<div class="text-end mb-2">
<?php $new_group_form->printHTML(); ?>
</div>
<?php endif ?>
</div>
<!-- end title container -->
<?php
$groups_caption = UOJLocale::get('groups');
$users_caption = UOJLocale::get('users count');
$header = <<<EOD
<tr> <tr>
<th class="text-center" style="width:5em;">ID</th> <th class="text-center" style="width:5em;">ID</th>
<th>{$groups_caption}</th> <th>{$groups_caption}</th>
@ -59,52 +52,58 @@
</tr> </tr>
EOD; EOD;
if (isSuperUser($myUser)) { if (isSuperUser(Auth::user())) {
$cond = "1"; $cond = "1";
} else { } else {
$cond = 'is_hidden = 0'; $cond = ["is_hidden" => false];
} }
echoLongTable( echoLongTable(
['a.id as group_id', 'a.title as title', 'a.is_hidden as is_hidden', 'count(b.username) as user_count'], ['id', 'title', 'is_hidden'],
"`groups` a left join groups_users b on a.id = b.group_id", "`groups`",
$cond, $cond,
'group by a.id order by a.id asc', 'order by id asc',
$header, $header,
function ($group) use ($myUser) { function ($group) {
echo '<tr class="text-center">'; $users_count = DB::selectCount([
echo '<td>'; "select count(*)",
echo '#', $group['group_id'], '</td>'; "from", "groups_users",
"where", [
echo '<td class="text-start">'; "group_id" => $group['id'],
echo '<a class="text-decoration-none" href="/group/', $group['group_id'], '">', $group['title'], '</a>'; ],
if ($group['is_hidden']) { ]);
echo ' <span class="badge text-bg-danger"><i class="bi bi-eye-slash-fill"></i> ', UOJLocale::get('hidden'), '</span> ';
}
echo '</td>';
echo "<td>{$group['user_count']}</td>";
echo '</tr>';
},
[
'page_len' => 40,
'div_classes' => ['card', 'my-3'],
'table_classes' => ['table', 'uoj-table', 'mb-0'],
'head_pagination' => true,
'pagination_table' => "`groups`",
]
);
?>
<!-- end left col --> echo '<tr class="text-center">';
</div> echo '<td>';
echo '#', $group['id'], '</td>';
<!-- right col --> echo '<td class="text-start">';
<aside class="col-lg-3 mt-3 mt-lg-0"> echo '<a class="text-decoration-none" href="/group/', $group['id'], '">', $group['title'], '</a>';
<?php uojIncludeView('sidebar', array()); ?> if ($group['is_hidden']) {
</aside> echo ' <span class="badge text-bg-danger"><i class="bi bi-eye-slash-fill"></i> ', UOJLocale::get('hidden'), '</span> ';
}
echo '</td>';
echo "<td>{$users_count}</td>";
echo '</tr>';
},
[
'page_len' => 40,
'div_classes' => ['card', 'my-3'],
'table_classes' => ['table', 'uoj-table', 'mb-0'],
'head_pagination' => true,
]
);
?>
<!-- end left col -->
</div>
<!-- right col -->
<aside class="col-lg-3 mt-3 mt-lg-0">
<?php uojIncludeView('sidebar') ?>
</aside>
<!-- end right col -->
</div> </div>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,101 +1,117 @@
<?php <?php
requirePHPLib('form'); requirePHPLib('form');
requireLib('bootstrap5'); requirePHPLib('judger');
requireLib('hljs'); requirePHPLib('data');
requireLib('bootstrap5');
requireLib('hljs');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin(); UOJHack::init(UOJRequest::get('id')) || UOJResponse::page404();
} UOJHack::cur()->setProblem() || UOJResponse::page404();
UOJHack::cur()->userCanView(Auth::user(), ['ensure' => true]);
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { if (UOJHack::cur()->setSubmission()) {
become403Page(); UOJHack::cur()->submission->setAsCur();
} UOJSubmission::cur()->setProblem(['problem' => UOJHack::cur()->problem]) || UOJResponse::page404();
UOJSubmission::cur()->userCanView(Auth::user(), ['ensure' => true]);
}
if (!validateUInt($_GET['id']) || !($hack = queryHack($_GET['id']))) { if (UOJHack::cur()->userCanDelete(Auth::user())) {
become404Page(); $delete_form = new UOJBs4Form('delete');
} $delete_form->handle = function () {
DB::delete([
"delete from hacks",
"where", ["id" => UOJHack::info('id')]
]);
};
$delete_form->submit_button_config['class_str'] = 'btn btn-danger';
$delete_form->submit_button_config['text'] = '删除此 Hack';
$delete_form->submit_button_config['align'] = 'end';
$delete_form->submit_button_config['smart_confirm'] = '';
$delete_form->succ_href = "/hacks";
$delete_form->runAtServer();
}
$submission = querySubmission($hack['submission_id']); if (UOJHack::cur()->userCanReview(Auth::user())) {
$problem = queryProblemBrief($submission['problem_id']); $addex_form = new UOJBs4Form('addex');
$problem_extra_config = getProblemExtraConfig($problem); $addex_form->handle = function () {
$input = UOJContext::storagePath() . UOJHack::info('input');
$new_in = "{$input}_in";
$new_out = "{$input}_out";
$reason = null;
$err = dataAddHackPoint(UOJHack::cur()->problem->info, $new_in, $new_out, $reason, Auth::user());
$err === '' || UOJResponse::message($err);
unlink($new_in);
unlink($new_out);
DB::update([
"update hacks",
"set", [
'status' => 'Judged',
], "where", ['id' => UOJHack::info('id')]
]);
};
$addex_form->submit_button_config['class_str'] = 'btn btn-danger mt-3';
$addex_form->submit_button_config['text'] = '确认无误,添加到测试数据';
$addex_form->submit_button_config['align'] = 'end';
$addex_form->submit_button_config['smart_confirm'] = '';
$addex_form->succ_href = "/hacks";
$addex_form->runAtServer();
}
if ($submission['contest_id']) { $perm = UOJHack::cur()->viewerCanSeeComponents(Auth::user());
$contest = queryContest($submission['contest_id']); ?>
genMoreContestInfo($contest); <?php echoUOJPageHeader(UOJLocale::get('problems::hack') . ' #' . UOJHack::info('id')) ?>
} else {
$contest = null;
}
if (!isHackVisibleToUser($hack, $problem, $myUser)) { <h1>
become403Page(); <?= UOJLocale::get('problems::hack') . ' #' . UOJHack::info('id') ?>
}
if (isSuperUser($myUser)) {
$delete_form = new UOJForm('delete');
$delete_form->handle = function() {
global $hack;
DB::query("delete from hacks where id = {$hack['id']}");
};
$delete_form->submit_button_config['class_str'] = 'btn btn-danger';
$delete_form->submit_button_config['text'] = '删除此Hack';
$delete_form->submit_button_config['align'] = 'right';
$delete_form->submit_button_config['smart_confirm'] = '';
$delete_form->succ_href = "/hacks";
$delete_form->runAtServer();
}
$should_show_content = hasViewPermission($problem_extra_config['view_content_type'], $myUser, $problem, $submission);
$should_show_all_details = hasViewPermission($problem_extra_config['view_all_details_type'], $myUser, $problem, $submission);
$should_show_details = hasViewPermission($problem_extra_config['view_details_type'], $myUser, $problem, $submission);
$should_show_details_to_me = isSuperUser($myUser);
if ($hack['success'] === null) {
$should_show_all_details = false;
}
if (!isSubmissionFullVisibleToUser($submission, $contest, $problem, $myUser)
|| !isHackFullVisibleToUser($hack, $contest, $problem, $myUser)) {
$should_show_content = $should_show_all_details = false;
}
if ($should_show_all_details) {
$styler = new HackDetailsStyler();
if (!$should_show_details) {
$styler->fade_all_details = true;
$styler->show_small_tip = false;
}
}
?>
<?php echoUOJPageHeader(UOJLocale::get('problems::hack').' #'.$hack['id']) ?>
<h1 class="h3">
<?= UOJLocale::get('problems::hack').' #'.$hack['id'] ?>
</h1> </h1>
<?php echoHackListOnlyOne($hack, array('id_hidden' => ''), $myUser) ?> <?php echoHackListOnlyOne(UOJHack::info(), [], Auth::user()) ?>
<?php if ($should_show_all_details): ?>
<div class="card border-info mb-3"> <?php if (UOJHack::cur()->hasJudged()) : ?>
<div class="card-header bg-info"> <?php if ($perm['high_level_details']) : ?>
<h4 class="card-title"><?= UOJLocale::get('details') ?></h4> <div class="card mb-3">
<div class="card-header fw-bold">
<?= UOJLocale::get('details') ?>
</div>
<div class="card-body p-0">
<?php
$styler = new HackDetailsStyler();
if (!$perm['low_level_details']) {
$styler->fade_all_details = true;
$styler->show_small_tip = false;
}
echoJudgmentDetails(UOJHack::info('details'), $styler, 'details');
?>
<?php if ($perm['manager_view'] && !$perm['low_level_details']) : ?>
<hr />
<h4 class="text-info">全部详细信息(仅管理员可见)</h4>
<?php echoHackDetails(UOJHack::info('details'), 'all_details') ?>
<?php endif ?>
</div>
</div> </div>
<?php echoJudgementDetails($hack['details'], $styler, 'details') ?> <?php endif ?>
<?php if ($should_show_details_to_me): ?>
<?php if ($styler->fade_all_details): ?>
<hr />
<?php echoHackDetails($hack['details'], 'final_details') ?>
<?php endif ?>
<?php endif ?>
</div>
<?php endif ?> <?php endif ?>
<h2 class="h3"> <h2 class="mt-3">
<?= UOJLocale::get('problems::submission').' #'.$submission['id'] ?> <?= UOJLocale::get('problems::submission') . ' #' . UOJSubmission::info('id') ?>
</h2> </h2>
<?php echoSubmissionsListOnlyOne($submission, array(), $myUser) ?> <?php if (UOJSubmission::cur()) : ?>
<?php if ($should_show_content): ?> <?php UOJSubmission::cur()->echoStatusTable(['show_actual_score' => $perm['score']], Auth::user()) ?>
<?php echoSubmissionContent($submission, getProblemSubmissionRequirement($problem)) ?> <?php if ($perm['content'] || $perm['manager_view']) : ?>
<?php UOJSubmission::cur()->echoContent() ?>
<?php endif ?>
<?php else : ?>
<h3 class="text-danger">提交记录信息损坏</h3>
<?php endif ?> <?php endif ?>
<?php if (isset($delete_form)): ?> <?php if (isset($delete_form)) : ?>
<?php $delete_form->printHTML() ?> <?php $delete_form->printHTML() ?>
<?php endif ?> <?php endif ?>
<?php if (isset($addex_form)) : ?>
<?php $addex_form->printHTML() ?>
<?php endif ?>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,60 +1,54 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin();
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { $q_problem_id = isset($_GET['problem_id']) && validateUInt($_GET['problem_id']) ? $_GET['problem_id'] : null;
become403Page(); $q_submission_id = isset($_GET['submission_id']) && validateUInt($_GET['submission_id']) ? $_GET['submission_id'] : null;
} $q_hacker = isset($_GET['hacker']) && validateUsername($_GET['hacker']) ? $_GET['hacker'] : null;
$q_owner = isset($_GET['owner']) && validateUsername($_GET['owner']) ? $_GET['owner'] : null;
$conds = array(); $conds = [];
if ($q_problem_id != null) {
$conds[] = ["problem_id" => $q_problem_id];
}
if ($q_submission_id != null) {
$conds[] = ["submission_id" => $q_submission_id];
}
if ($q_hacker != null) {
$conds[] = ["hacker" => $q_hacker];
}
if ($q_owner != null) {
$conds[] = ["owner" => $q_owner];
}
$q_problem_id = isset($_GET['problem_id']) && validateUInt($_GET['problem_id']) ? $_GET['problem_id'] : null; $selected_all = ' selected="selected"';
$q_submission_id = isset($_GET['submission_id']) && validateUInt($_GET['submission_id']) ? $_GET['submission_id'] : null; $selected_succ = '';
$q_hacker = isset($_GET['hacker']) && validateUsername($_GET['hacker']) ? $_GET['hacker'] : null; $selected_fail = '';
$q_owner = isset($_GET['owner']) && validateUsername($_GET['owner']) ? $_GET['owner'] : null; if (isset($_GET['status']) && validateUInt($_GET['status'])) {
if ($q_problem_id != null) { if ($_GET['status'] == 1) {
$conds[] = "problem_id = $q_problem_id"; $selected_all = '';
$selected_succ = ' selected="selected"';
$conds[] = 'success = 1';
} }
if ($q_submission_id != null) { if ($_GET['status'] == 2) {
$conds[] = "submission_id = $q_submission_id"; $selected_all = '';
$selected_fail = ' selected="selected"';
$conds[] = 'success = 0';
} }
if ($q_hacker != null) { }
$conds[] = "hacker = '$q_hacker'";
} if ($conds) {
if ($q_owner != null) { $cond = $conds;
$conds[] = "owner = '$q_owner'"; } else {
} $cond = '1';
}
$selected_all = ' selected="selected"';
$selected_succ =''; ?>
$selected_fail ='';
if (isset($_GET['status']) && validateUInt($_GET['status'])) {
if ($_GET['status'] == 1) {
$selected_all = '';
$selected_succ =' selected="selected"';
$conds[] = 'success = 1';
}
if ($_GET['status'] == 2) {
$selected_all = '';
$selected_fail = ' selected="selected"';
$conds[] = 'success = 0';
}
}
if ($conds) {
$cond = join($conds, ' and ');
} else {
$cond = '1';
}
?>
<?php echoUOJPageHeader(UOJLocale::get('hacks')) ?> <?php echoUOJPageHeader(UOJLocale::get('hacks')) ?>
<h1 class="h2"> <h1>
<?= UOJLocale::get('hacks') ?> <?= UOJLocale::get('hacks') ?>
</h1> </h1>
<div class="d-none d-sm-block mb-3"> <div class="d-none d-sm-block mb-3">
@ -77,10 +71,10 @@
</label> </label>
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control form-control-sm" name="hacker" id="input-hacker" value="<?= $q_hacker ?>" maxlength="20" style="width:10em" /> <input type="text" class="form-control form-control-sm" name="hacker" id="input-hacker" value="<?= $q_hacker ?>" maxlength="20" style="width:10em" />
<?php if (Auth::check()): ?> <?php if (Auth::check()) : ?>
<a id="my-hacks" href="/hacks?hacker=<?= Auth::id() ?>" class="btn btn-outline-secondary btn-sm"> <a id="my-hacks" href="/hacks?hacker=<?= Auth::id() ?>" class="btn btn-outline-secondary btn-sm">
我的 我的
</a> </a>
<?php endif ?> <?php endif ?>
</div> </div>
<script> <script>
@ -97,10 +91,10 @@
</label> </label>
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control form-control-sm" name="owner" id="input-owner" value="<?= $q_owner ?>" maxlength="20" style="width:10em" /> <input type="text" class="form-control form-control-sm" name="owner" id="input-owner" value="<?= $q_owner ?>" maxlength="20" style="width:10em" />
<?php if (Auth::check()): ?> <?php if (Auth::check()) : ?>
<a id="my-owners" href="/hacks?owner=<?= Auth::id() ?>" class="btn btn-outline-secondary btn-sm"> <a id="my-owners" href="/hacks?owner=<?= Auth::id() ?>" class="btn btn-outline-secondary btn-sm">
我的 我的
</a> </a>
<?php endif ?> <?php endif ?>
</div> </div>
<script> <script>
@ -116,9 +110,9 @@
<?= UOJLocale::get('problems::result') ?>: <?= UOJLocale::get('problems::result') ?>:
</label> </label>
<select class="form-select form-select-sm" id="input-status" name="status"> <select class="form-select form-select-sm" id="input-status" name="status">
<option value=""<?= $selected_all?>>All</option> <option value="" <?= $selected_all ?>>All</option>
<option value="1"<?= $selected_succ ?>>Success!</option> <option value="1" <?= $selected_succ ?>>Success!</option>
<option value="2"<?= $selected_fail ?>>Failed.</option> <option value="2" <?= $selected_fail ?>>Failed.</option>
</select> </select>
</div> </div>
<div class="col-auto"> <div class="col-auto">
@ -129,7 +123,9 @@
</form> </form>
</div> </div>
<?php echoHacksList($cond, <?php
echoHacksList(
$cond,
'order by id desc', 'order by id desc',
[ [
'judge_time_hidden' => '', 'judge_time_hidden' => '',
@ -138,7 +134,8 @@
'table_classes' => ['table', 'mb-0', 'uoj-table', 'text-center'], 'table_classes' => ['table', 'mb-0', 'uoj-table', 'text-center'],
], ],
], ],
$myUser); Auth::user()
?> );
?>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,19 +1,18 @@
<?php <?php requireLib('bootstrap5') ?>
requireLib('bootstrap5');
?>
<?php echoUOJPageHeader(UOJLocale::get('html to markdown')) ?> <?php echoUOJPageHeader(UOJLocale::get('html to markdown')) ?>
<h1 class="h2"> <h1>
<?= UOJLocale::get('html to markdown') ?> <?= UOJLocale::get('html to markdown') ?>
</h1> </h1>
<style> <style>
#html, #markdown { #html,
font-family: Cascadia Mono, Ubuntu Mono, Roboto Mono, Jetbrains Mono, Fira Code, Consolas, '思源黑体 Regular', '思源宋体 Light', '宋体', 'Courier New', monospace; #markdown {
width: 100%; font-family: Cascadia Mono, Ubuntu Mono, Roboto Mono, Jetbrains Mono, Fira Code, Consolas, '思源黑体 Regular', '思源宋体 Light', '宋体', 'Courier New', monospace;
min-height: 300px; width: 100%;
} min-height: 300px;
}
</style> </style>
<div class="card"> <div class="card">
@ -35,11 +34,13 @@
<?= HTML::js_src('/js/h2m.js') ?> <?= HTML::js_src('/js/h2m.js') ?>
<script> <script>
$(document).ready(function() { $(document).ready(function() {
$('#html').on('input', function() { $('#html').on('input', function() {
$('#markdown').val(h2m($('#html').val(), { converter: 'Gfm' })); $('#markdown').val(h2m($('#html').val(), {
converter: 'Gfm'
}));
});
}); });
});
</script> </script>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,24 +1,23 @@
<?php <?php
requirePHPLib('form');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { if (!Auth::check()) {
become403Page(UOJLocale::get('need login')); become403Page(UOJLocale::get('need login'));
} }
$name = $_GET['image_name']; $name = $_GET['image_name'];
if (!validateString($name)) { if (!validateString($name)) {
become404Page(); become404Page();
} }
$file_name = UOJContext::storagePath()."/image_hosting/$name.png"; $file_name = UOJContext::storagePath() . "/image_hosting/$name.png";
$finfo = finfo_open(FILEINFO_MIME); $finfo = finfo_open(FILEINFO_MIME);
$mimetype = finfo_file($finfo, $file_name); $mimetype = finfo_file($finfo, $file_name);
if ($mimetype === false) { if ($mimetype === false) {
become404Page(); become404Page();
} }
finfo_close($finfo); finfo_close($finfo);
header("X-Sendfile: $file_name"); header("X-Sendfile: $file_name");
header("Content-type: $mimetype"); header("Content-type: $mimetype");
header("Cache-Control: max-age=604800", true); header("Cache-Control: max-age=604800", true);

View File

@ -1,54 +1,60 @@
<?php <?php
use Gregwar\Captcha\PhraseBuilder;
use Gregwar\Captcha\CaptchaBuilder;
requirePHPLib('form'); use Gregwar\Captcha\PhraseBuilder;
requireLib('bootstrap5');
if (!Auth::check()) { requirePHPLib('form');
redirectToLogin(); requireLib('bootstrap5');
Auth::check() || redirectToLogin();
$extra = UOJUser::getExtra($user);
$limit = $extra['image_hosting']['total_size_limit'];
$used = DB::selectSingle([
"select sum(size)",
"from users_images",
"where", [
"uploader" => Auth::id(),
],
]);
$count = DB::selectCount([
"select count(*)",
"from users_images",
"where", [
"uploader" => Auth::id(),
],
]);
function throwError($msg) {
dieWithJsonData(['status' => 'error', 'message' => $msg]);
}
$allowedTypes = [IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_WEBP];
if ($_POST['image_upload_file_submit'] == 'submit') {
if (!crsf_check()) {
throwError('expired');
} }
if (!isNormalUser($myUser)) { if (!isset($_SESSION['phrase']) || !PhraseBuilder::comparePhrases($_SESSION['phrase'], $_POST['captcha'])) {
become403Page(); throwError("bad_captcha");
} }
$limit = $myUser['images_size_limit']; if ($_FILES["image_upload_file"]["error"] > 0) {
$_result = DB::selectFirst("SELECT SUM(size), count(*) FROM `users_images` WHERE uploader = '{$myUser['username']}'"); throwError($_FILES["image_upload_file"]["error"]);
$used = $_result["SUM(size)"];
$count = $_result["count(*)"];
function throwError($msg) {
dieWithJsonData(['status' => 'error', 'message' => $msg]);
} }
$allowedTypes = [IMAGETYPE_PNG, IMAGETYPE_JPEG]; if ($_FILES["image_upload_file"]["size"] > 5242880) { // 5 MB
if ($_POST['image_upload_file_submit'] == 'submit') { throwError('too_large');
if (!crsf_check()) { }
throwError('expired');
}
if (!isset($_SESSION['phrase']) || !PhraseBuilder::comparePhrases($_SESSION['phrase'], $_POST['captcha'])) { if ($used + $_FILES["image_upload_file"]["size"] > $limit) {
throwError("bad_captcha"); throwError('storage_limit_exceeded');
} }
if ($_FILES["image_upload_file"]["error"] > 0) { $size = getimagesize($_FILES['image_upload_file']['tmp_name']);
throwError($_FILES["image_upload_file"]["error"]);
}
if ($_FILES["image_upload_file"]["size"] > 5242880) { // 5 MB if (!$size || !in_array($size[2], $allowedTypes)) {
throwError('too_large'); throwError('not_a_image');
} }
if ($used + $_FILES["image_upload_file"]["size"] > $limit) {
throwError('storage_limit_exceeded');
}
$size = getimagesize($_FILES['image_upload_file']['tmp_name']);
if (!$size || !in_array($size[2], $allowedTypes)) {
throwError('not_a_image');
}
list($width, $height, $type) = $size; list($width, $height, $type) = $size;
$hash = hash_file("sha256", $_FILES['image_upload_file']['tmp_name']) . Auth::id(); $hash = hash_file("sha256", $_FILES['image_upload_file']['tmp_name']) . Auth::id();
@ -59,177 +65,177 @@
$watermark_text = ""; $watermark_text = "";
$hash .= "__no_watermark"; $hash .= "__no_watermark";
} elseif ($_POST['watermark'] == 'site_shortname_and_username') { } elseif ($_POST['watermark'] == 'site_shortname_and_username') {
$watermark_text .= ' @'.Auth::id(); $watermark_text .= ' @' . Auth::id();
$hash .= "__id"; $hash .= "__id";
} }
$existing_image = DB::selectFirst("SELECT * FROM users_images WHERE `hash` = '$hash'"); $existing_image = DB::selectFirst("SELECT * FROM users_images WHERE `hash` = '$hash'");
if ($existing_image) { if ($existing_image) {
dieWithJsonData(['status' => 'success', 'path' => $existing_image['path']]); dieWithJsonData(['status' => 'success', 'path' => $existing_image['path']]);
} }
$image = new Imagick($_FILES["image_upload_file"]["tmp_name"]); $image = new Imagick($_FILES["image_upload_file"]["tmp_name"]);
$draw = new ImagickDraw(); $draw = new ImagickDraw();
$draw->setFont(UOJContext::documentRoot().'/fonts/roboto-mono/RobotoMono-Bold.ttf'); $draw->setFont(UOJContext::documentRoot() . '/fonts/roboto-mono/RobotoMono-Bold.ttf');
$draw->setFontSize($scale * 14); $draw->setFontSize($scale * 14);
$draw->setGravity(Imagick::GRAVITY_SOUTHEAST); $draw->setGravity(Imagick::GRAVITY_SOUTHEAST);
$draw->setFillColor("rgba(100,100,100,0.5)"); $draw->setFillColor("rgba(100,100,100,0.5)");
$image->annotateImage($draw, 15, 10, 0, $watermark_text); $image->annotateImage($draw, 15, 10, 0, $watermark_text);
$draw->setFillColor("rgba(255,255,255,0.65)"); $draw->setFillColor("rgba(255,255,255,0.65)");
$image->annotateImage($draw, 15 + $scale, 10 + $scale, 0, $watermark_text); $image->annotateImage($draw, 15 + $scale, 10 + $scale, 0, $watermark_text);
$image->setImageFormat('png'); $image->setImageFormat('png');
$image->writeImage(); $image->writeImage();
if (($size = filesize($_FILES["image_upload_file"]["tmp_name"])) > 5242880) { // 5 MB if (($size = filesize($_FILES["image_upload_file"]["tmp_name"])) > 5242880) { // 5 MB
throwError('too_large'); throwError('too_large');
} }
$filename = uojRandAvaiableFileName('/image_hosting/', 10, '.png'); $filename = uojRandAvaiableFileName('/image_hosting/', 10, '.png');
if (!move_uploaded_file($_FILES["image_upload_file"]["tmp_name"], UOJContext::storagePath().$filename)) { if (!move_uploaded_file($_FILES["image_upload_file"]["tmp_name"], UOJContext::storagePath() . $filename)) {
throwError('unknown error'); throwError('unknown error');
} }
DB::insert("INSERT INTO users_images (`path`, uploader, width, height, upload_time, size, `hash`) VALUES ('$filename', '{$myUser['username']}', $width, $height, now(), {$_FILES["image_upload_file"]["size"]}, '$hash')"); DB::insert("INSERT INTO users_images (`path`, uploader, width, height, upload_time, size, `hash`) VALUES ('$filename', '{$myUser['username']}', $width, $height, now(), {$_FILES["image_upload_file"]["size"]}, '$hash')");
dieWithJsonData(['status' => 'success', 'path' => $filename]); dieWithJsonData(['status' => 'success', 'path' => $filename]);
} elseif ($_POST['image_delete_submit'] == 'submit') { } elseif ($_POST['image_delete_submit'] == 'submit') {
crsf_defend(); crsf_defend();
$id = $_POST['image_delete_id']; $id = $_POST['image_delete_id'];
if (!validateUInt($id)) { if (!validateUInt($id)) {
becomeMsgPage('ID 不合法。<a href="'.UOJContext::requestURI().'">返回</a>'); becomeMsgPage('ID 不合法。<a href="' . UOJContext::requestURI() . '">返回</a>');
} else {
$result = DB::selectFirst("SELECT * from users_images WHERE id = $id");
if (!$result) {
becomeMsgPage('图片不存在。<a href="' . UOJContext::requestURI() . '">返回</a>');
} else { } else {
$result = DB::selectFirst("SELECT * from users_images WHERE id = $id"); unlink(UOJContext::storagePath() . $result['path']);
if (!$result) { DB::delete("DELETE FROM users_images WHERE id = $id");
becomeMsgPage('图片不存在。<a href="'.UOJContext::requestURI().'">返回</a>');
} else {
unlink(UOJContext::storagePath().$result['path']);
DB::delete("DELETE FROM users_images WHERE id = $id");
header("Location: ". UOJContext::requestURI()); header("Location: " . UOJContext::requestURI());
die(); die();
}
} }
} }
?> }
?>
<?php echoUOJPageHeader(UOJLocale::get('image hosting')) ?> <?php echoUOJPageHeader(UOJLocale::get('image hosting')) ?>
<style> <style>
.drop { .drop {
display: flex; display: flex;
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-self: center; align-self: center;
flex-grow: 0 !important; flex-grow: 0 !important;
width: 9em; width: 9em;
height: 8.75em; height: 8.75em;
user-select: none; user-select: none;
cursor: pointer; cursor: pointer;
margin-left: 0; margin-left: 0;
background: #fafafa; background: #fafafa;
border: 1px solid #e8e8e8; border: 1px solid #e8e8e8;
box-sizing: border-box; box-sizing: border-box;
border-radius: 4px; border-radius: 4px;
} }
.drop:hover { .drop:hover {
border-color: #89d1f5; border-color: #89d1f5;
} }
</style> </style>
<h1 class="h2"> <h1>
<?= UOJLocale::get('image hosting') ?> <?= UOJLocale::get('image hosting') ?>
</h1> </h1>
<div class="card card-default"> <div class="card card-default">
<div class="card-body"> <div class="card-body">
<form class="row m-0" id="image-upload-form" method="post" enctype="multipart/form-data"> <form class="row m-0" id="image-upload-form" method="post" enctype="multipart/form-data">
<div class="col-12 col-md-3 col-lg-3 order-1 drop mx-auto mx-md-0" id="image-upload-form-drop"> <div class="col-12 col-md-3 col-lg-3 order-1 drop mx-auto mx-md-0" id="image-upload-form-drop">
<svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" width="56" class="mb-2"> <svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" width="56" class="mb-2">
<g> <g>
<path fill="#3498db" d="M424.49 120.48a12 12 0 0 0-17 0L272 256l-39.51-39.52a12 12 0 0 0-17 0L160 272v48h352V208zM64 336V128H48a48 48 0 0 0-48 48v256a48 48 0 0 0 48 48h384a48 48 0 0 0 48-48v-16H144a80.09 80.09 0 0 1-80-80z"></path> <path fill="#3498db" d="M424.49 120.48a12 12 0 0 0-17 0L272 256l-39.51-39.52a12 12 0 0 0-17 0L160 272v48h352V208zM64 336V128H48a48 48 0 0 0-48 48v256a48 48 0 0 0 48 48h384a48 48 0 0 0 48-48v-16H144a80.09 80.09 0 0 1-80-80z"></path>
<path fill="#89d1f5" d="M528 32H144a48 48 0 0 0-48 48v256a48 48 0 0 0 48 48h384a48 48 0 0 0 48-48V80a48 48 0 0 0-48-48zM208 80a48 48 0 1 1-48 48 48 48 0 0 1 48-48zm304 240H160v-48l55.52-55.52a12 12 0 0 1 17 0L272 256l135.52-135.52a12 12 0 0 1 17 0L512 208z"></path> <path fill="#89d1f5" d="M528 32H144a48 48 0 0 0-48 48v256a48 48 0 0 0 48 48h384a48 48 0 0 0 48-48V80a48 48 0 0 0-48-48zM208 80a48 48 0 1 1-48 48 48 48 0 0 1 48-48zm304 240H160v-48l55.52-55.52a12 12 0 0 1 17 0L272 256l135.52-135.52a12 12 0 0 1 17 0L512 208z"></path>
</g> </g>
</svg> </svg>
<span id="select-image-text" class="small">点击此处选择图片</span> <span id="select-image-text" class="small">点击此处选择图片</span>
</div> </div>
<input id="image_upload_file" name="image_upload_file" type="file" accept="image/*" style="display: none;" /> <input id="image_upload_file" name="image_upload_file" type="file" accept="image/*" style="display: none;" />
<div class="modal fade" id="image-upload-modal" tabindex="-1" aria-hidden="true"> <div class="modal fade" id="image-upload-modal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">上传图片</h1> <h1 class="modal-title fs-5" id="exampleModalLabel">上传图片</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
您确定要上传图片吗?
</div> </div>
<div class="mb-3" id="modal-file-info"></div> <div class="modal-body">
<div class="input-group"> <div class="mb-3">
<input type="text" class="form-control" id="input-captcha" name="captcha" placeholder="<?= UOJLocale::get('enter verification code') ?>" maxlength="20" /> 您确定要上传图片吗?
<span class="input-group-text p-0 overflow-hidden rounded-0" style="border-bottom-right-radius: var(--bs-border-radius) !important"> </div>
<img id="captcha" class="col w-100 h-100" src="/captcha"> <div class="mb-3" id="modal-file-info"></div>
</span> <div class="input-group">
<input type="text" class="form-control" id="input-captcha" name="captcha" placeholder="<?= UOJLocale::get('enter verification code') ?>" maxlength="20" />
<span class="input-group-text p-0 overflow-hidden rounded-0" style="border-bottom-right-radius: var(--bs-border-radius) !important">
<img id="captcha" class="col w-100 h-100" src="/captcha">
</span>
</div>
<div class="mt-3" id="modal-help-message" style="display: none"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" id="cancel-upload">取消</button>
<button type="submit" class="btn btn-primary">确定</button>
</div> </div>
<div class="mt-3" id="modal-help-message" style="display: none"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" id="cancel-upload">取消</button>
<button type="submit" class="btn btn-primary">确定</button>
</div> </div>
</div> </div>
</div> </div>
</div> <div class="col-12 col-md-4 col-lg-2 order-2 mt-3 mt-md-0 ms-md-2">
<div class="col-12 col-md-4 col-lg-2 order-2 mt-3 mt-md-0 ms-md-2"> <h2 class="h3">水印</h2>
<h2 class="h4">水印</h2> <?php if (isSuperUser($myUser)) : ?>
<?php if (isSuperUser($myUser)): ?> <div class="form-check d-inline-block d-md-block me-2">
<div class="form-check d-inline-block d-md-block me-2"> <input class="form-check-input" type="radio" name="watermark" id="watermark-no_watermark" data-value="no_watermark">
<input class="form-check-input" type="radio" name="watermark" id="watermark-no_watermark" data-value="no_watermark"> <label class="form-check-label" for="watermark-no_watermark">
<label class="form-check-label" for="watermark-no_watermark"> 无水印
无水印 </label>
</label> </div>
<?php endif ?>
<div class="form-check d-inline-block d-md-block me-2">
<input class="form-check-input" type="radio" name="watermark" id="watermark-site_shortname" data-value="site_shortname" checked>
<label class="form-check-label" for="watermark-site_shortname">
<?= UOJConfig::$data['profile']['oj-name-short'] ?>
</label>
</div>
<div class="form-check d-inline-block d-md-block me-2">
<input class="form-check-input" type="radio" name="watermark" id="watermark-site_shortname_and_username" data-value="site_shortname_and_username">
<label class="form-check-label" for="watermark-site_shortname_and_username">
<?= UOJConfig::$data['profile']['oj-name-short'] ?> @<?= Auth::id() ?>
</label>
</div>
</div> </div>
<?php endif ?> <div class="col order-3 order-md-4 order-lg-3 mt-3 mt-lg-0 ms-lg-2">
<div class="form-check d-inline-block d-md-block me-2"> <h2 class="h3">上传须知</h2>
<input class="form-check-input" type="radio" name="watermark" id="watermark-site_shortname" data-value="site_shortname" checked> <ul>
<label class="form-check-label" for="watermark-site_shortname"> <li>上传的图片必须符合法律与社会道德;</li>
<?= UOJConfig::$data['profile']['oj-name-short'] ?> <li>图床仅供 S2OJ 站内使用,校外用户无法查看;</li>
</label> <li>图片上传后会被自动转码为 PNG 格式;</li>
<li>在合适的地方插入图片即可引用。</li>
</ul>
<p class="small">更多信息可以查看 <a href="https://s2oj.github.io/#/user/apps/image_hosting" target="_blank">使用文档</a></p>
</div> </div>
<div class="form-check d-inline-block d-md-block me-2"> <div class="col-12 col-md-5 col-lg-3 order-4 order-md-3 order-lg-4 mt-3 mt-md-0 ms-md-2">
<input class="form-check-input" type="radio" name="watermark" id="watermark-site_shortname_and_username" data-value="site_shortname_and_username"> <h2 class="h3">使用统计</h2>
<label class="form-check-label" for="watermark-site_shortname_and_username"> <div class="d-flex justify-content-between">
<?= UOJConfig::$data['profile']['oj-name-short'] ?> @<?= Auth::id() ?> <span class="small">已用空间</span>
</label> <span><?= round($used * 1.0 / 1024 / 1024, 2) ?> MB / <?= round($limit * 1.0 / 1024 / 1024, 2) ?> MB</span>
</div>
<div class="d-flex justify-content-between">
<span class="small">上传总数</span>
<span><?= $count ?> 张</span>
</div>
</div> </div>
</div> </form>
<div class="col order-3 order-md-4 order-lg-3 mt-3 mt-lg-0 ms-lg-2"> </div>
<h2 class="h4">上传须知</h2>
<ul>
<li>上传的图片必须符合法律与社会道德;</li>
<li>图床仅供 S2OJ 站内使用,校外用户无法查看;</li>
<li>图片上传后会被自动转码为 PNG 格式;</li>
<li>在合适的地方插入图片即可引用。</li>
</ul>
<p class="small">更多信息可以查看 <a href="https://s2oj.github.io/#/user/apps/image_hosting" target="_blank">使用文档</a></p>
</div>
<div class="col-12 col-md-5 col-lg-3 order-4 order-md-3 order-lg-4 mt-3 mt-md-0 ms-md-2">
<h2 class="h4">使用统计</h2>
<div class="d-flex justify-content-between">
<span class="small">已用空间</span>
<span><?= round($used * 1.0 / 1024 / 1024, 2) ?> MB / <?= round($limit * 1.0 / 1024 / 1024, 2) ?> MB</span>
</div>
<div class="d-flex justify-content-between">
<span class="small">上传总数</span>
<span><?= $count ?> 张</span>
</div>
</div>
</form>
</div>
</div> </div>
<div class="toast-container position-fixed bottom-0 start-0 ms-3 mb-4" style="z-index: 999999"> <div class="toast-container position-fixed bottom-0 start-0 ms-3 mb-4" style="z-index: 999999">
<div id="image-upload-toast" class="toast text-bg-danger align-items-center border-0" role="alert" aria-live="assertive" aria-atomic="true"> <div id="image-upload-toast" class="toast text-bg-danger align-items-center border-0" role="alert" aria-live="assertive" aria-atomic="true">
@ -242,130 +248,132 @@
</div> </div>
</div> </div>
<script> <script>
var image_upload_modal = new bootstrap.Modal('#image-upload-modal'); var image_upload_modal = new bootstrap.Modal('#image-upload-modal');
var image_upload_toast = new bootstrap.Toast('#image-upload-toast', { delay: 2000 }); var image_upload_toast = new bootstrap.Toast('#image-upload-toast', {
var droppedFiles = false; delay: 2000
});
var droppedFiles = false;
function refreshCaptcha() { function refreshCaptcha() {
var timestamp = new Date().getTime(); var timestamp = new Date().getTime();
$("#captcha").attr("src", "/captcha" + '?' + timestamp); $("#captcha").attr("src", "/captcha" + '?' + timestamp);
}
$("#captcha").click(function(e) {
refreshCaptcha();
});
$('#image-upload-form').submit(function(event) {
event.preventDefault();
var data = new FormData();
data.append('_token', "<?= crsf_token() ?>");
data.append('image_upload_file_submit', 'submit');
data.append('image_upload_file', $('#image_upload_file').prop('files')[0]);
data.append('watermark', $('input[name=watermark]:checked', this).data('value'));
data.append('captcha', $('#input-captcha').val());
if ($('#image_upload_file').prop('files')[0].size > 5242880) {
$('#modal-help-message').html('图片大小不能超过 5 MB。').show();
return false;
} }
$('#modal-help-message').html('上传中...').show(); $("#captcha").click(function(e) {
refreshCaptcha();
$.ajax({
method: 'POST',
processData: false,
contentType: false,
data: data,
success: function(data) {
if (data.status === 'success') {
image_upload_modal.hide();
location.reload();
} else {
if (data.message === 'bad_captcha') {
refreshCaptcha();
$('#modal-help-message').html('验证码错误。').show();
} else if (data.message === 'expired') {
$('#modal-help-message').html('页面过期,请刷新重试。').show();
} else if (data.message === 'storage_limit_exceeded') {
$('#modal-help-message').html('存储超限,请联系管理员提升限制。').show();
} else if (data.message === 'not_a_image') {
$('#modal-help-message').html('文件格式不受支持。').show();
} else if (data.message === 'too_large') {
$('#modal-help-message').html('图片大小不能超过 5 MB。').show();
}
}
},
error: function() {
$('#modal-help-message').html('上传失败,请刷新页面重试。').addClass('text-danger').show();
}
}); });
return false; $('#image-upload-form').submit(function(event) {
}); event.preventDefault();
$('#image-upload-form-drop').click(function() {
$('#image_upload_file').click();
});
$('#image-upload-form-drop').on('drag dragstart dragend dragover dragenter dragleave drop', function(e) {
e.preventDefault();
e.stopPropagation();
}).on('dragover dragenter', function() {
$('#select-image-text').html('松开以上传');
}).on('dragleave dragend drop', function() {
$('#select-image-text').html('点击此处选择图片');
}).on('drop', function(e) {
$('#image_upload_file').prop('files', e.originalEvent.dataTransfer.files);
$('#image_upload_file').trigger('change');
});
$(document).on('paste', function(e) {
if (e.originalEvent.clipboardData) {
$('#image_upload_file').prop('files', e.originalEvent.clipboardData.files);
}
$('#image_upload_file').trigger('change'); var data = new FormData();
}); data.append('_token', "<?= crsf_token() ?>");
$('#image-upload-modal').on('hide.bs.modal', function() { data.append('image_upload_file_submit', 'submit');
$('#image-upload-form').trigger('reset'); data.append('image_upload_file', $('#image_upload_file').prop('files')[0]);
}); data.append('watermark', $('input[name=watermark]:checked', this).data('value'));
$('#image_upload_file').change(function() { data.append('captcha', $('#input-captcha').val());
var items = $(this).prop('files');
var file = null;
if (items && items.length) { if ($('#image_upload_file').prop('files')[0].size > 5242880) {
for (var i = 0; i < items.length; i++) { $('#modal-help-message').html('图片大小不能超过 5 MB。').show();
if (items[i].type.indexOf('image') !== -1) {
file = items[i];
break;
}
}
}
if (file) { return false;
refreshCaptcha();
var watermark_type = $('input[name=watermark]:checked', '#image-upload-form').data('value');
var html = '';
html += '<p><img src="'+ URL.createObjectURL(file) +'" height="150" style="object-fit: contain"></p>';
html += '<p class="small">大小:<b>'+(file.size / 1024).toFixed(2)+'</b> KB。';
if (watermark_type === 'no_watermark') {
html += '不添加水印。';
} else if (watermark_type === 'site_shortname_and_username') {
html += '使用水印:<?= UOJConfig::$data['profile']['oj-name-short'] ?> @<?= Auth::id() ?>。';
} else {
html += '使用水印:<?= UOJConfig::$data['profile']['oj-name-short'] ?>。';
} }
html += '</p>'; $('#modal-help-message').html('上传中...').show();
$('#modal-file-info').html(html); $.ajax({
$('#modal-help-message').html('').hide(); method: 'POST',
image_upload_modal.show(); processData: false,
} else { contentType: false,
image_upload_toast.show(); data: data,
} success: function(data) {
}); if (data.status === 'success') {
image_upload_modal.hide();
location.reload();
} else {
if (data.message === 'bad_captcha') {
refreshCaptcha();
$('#modal-help-message').html('验证码错误。').show();
} else if (data.message === 'expired') {
$('#modal-help-message').html('页面过期,请刷新重试。').show();
} else if (data.message === 'storage_limit_exceeded') {
$('#modal-help-message').html('存储超限,请联系管理员提升限制。').show();
} else if (data.message === 'not_a_image') {
$('#modal-help-message').html('文件格式不受支持。').show();
} else if (data.message === 'too_large') {
$('#modal-help-message').html('图片大小不能超过 5 MB。').show();
}
}
},
error: function() {
$('#modal-help-message').html('上传失败,请刷新页面重试。').addClass('text-danger').show();
}
});
return false;
});
$('#image-upload-form-drop').click(function() {
$('#image_upload_file').click();
});
$('#image-upload-form-drop').on('drag dragstart dragend dragover dragenter dragleave drop', function(e) {
e.preventDefault();
e.stopPropagation();
}).on('dragover dragenter', function() {
$('#select-image-text').html('松开以上传');
}).on('dragleave dragend drop', function() {
$('#select-image-text').html('点击此处选择图片');
}).on('drop', function(e) {
$('#image_upload_file').prop('files', e.originalEvent.dataTransfer.files);
$('#image_upload_file').trigger('change');
});
$(document).on('paste', function(e) {
if (e.originalEvent.clipboardData) {
$('#image_upload_file').prop('files', e.originalEvent.clipboardData.files);
}
$('#image_upload_file').trigger('change');
});
$('#image-upload-modal').on('hide.bs.modal', function() {
$('#image-upload-form').trigger('reset');
});
$('#image_upload_file').change(function() {
var items = $(this).prop('files');
var file = null;
if (items && items.length) {
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
file = items[i];
break;
}
}
}
if (file) {
refreshCaptcha();
var watermark_type = $('input[name=watermark]:checked', '#image-upload-form').data('value');
var html = '';
html += '<p><img src="' + URL.createObjectURL(file) + '" height="150" style="object-fit: contain"></p>';
html += '<p class="small">大小:<b>' + (file.size / 1024).toFixed(2) + '</b> KB。';
if (watermark_type === 'no_watermark') {
html += '不添加水印。';
} else if (watermark_type === 'site_shortname_and_username') {
html += '使用水印:<?= UOJConfig::$data['profile']['oj-name-short'] ?> @<?= Auth::id() ?>。';
} else {
html += '使用水印:<?= UOJConfig::$data['profile']['oj-name-short'] ?>。';
}
html += '</p>';
$('#modal-file-info').html(html);
$('#modal-help-message').html('').hide();
image_upload_modal.show();
} else {
image_upload_toast.show();
}
});
</script> </script>
<?php <?php
@ -376,15 +384,15 @@ $pag_config = [
'cond' => "uploader = '{$myUser['username']}'", 'cond' => "uploader = '{$myUser['username']}'",
'tail' => 'order by upload_time desc', 'tail' => 'order by upload_time desc',
]; ];
$pag = new Paginator($pag_config); $pag = new Paginator($pag_config);
?> ?>
<h2 class="h3 mt-4 mb-3"> <h2 class="mt-4 mb-3">
我的图片 我的图片
</h2> </h2>
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 g-4"> <div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 g-4">
<?php foreach ($pag->get() as $idx => $row): ?> <?php foreach ($pag->get() as $idx => $row) : ?>
<div class="col"> <div class="col">
<div class="card"> <div class="card">
<img src="<?= $row['path'] ?>" class="card-img-top" height="200" style="object-fit: contain"> <img src="<?= $row['path'] ?>" class="card-img-top" height="200" style="object-fit: contain">
@ -392,9 +400,9 @@ $pag_config = [
<div class="d-flex flex-wrap justify-content-between"> <div class="d-flex flex-wrap justify-content-between">
<time><?= $row['upload_time'] ?></time> <time><?= $row['upload_time'] ?></time>
<span> <span>
<?php if ($row['size'] < 1024 * 512): ?> <?php if ($row['size'] < 1024 * 512) : ?>
<?= round($row['size'] * 1.0 / 1024, 1) ?> KB <?= round($row['size'] * 1.0 / 1024, 1) ?> KB
<?php else: ?> <?php else : ?>
<?= round($row['size'] * 1.0 / 1024 / 1024, 1) ?> MB <?= round($row['size'] * 1.0 / 1024 / 1024, 1) ?> MB
<?php endif ?> <?php endif ?>
</span> </span>
@ -423,7 +431,7 @@ $pag_config = [
<?php endforeach ?> <?php endforeach ?>
</div> </div>
<?php if ($pag->isEmpty()): ?> <?php if ($pag->isEmpty()) : ?>
<div class="mt-4 text-muted"> <div class="mt-4 text-muted">
<?= UOJLocale::get('none') ?> <?= UOJLocale::get('none') ?>
</div> </div>
@ -445,23 +453,25 @@ $pag_config = [
</div> </div>
<script> <script>
$(document).ready(function() { $(document).ready(function() {
[...document.querySelectorAll('[data-bs-toggle="tooltip"]')].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); [...document.querySelectorAll('[data-bs-toggle="tooltip"]')].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
}); });
var copy_url_toast = new bootstrap.Toast('#copy-url-toast', { delay: 2000 }); var copy_url_toast = new bootstrap.Toast('#copy-url-toast', {
delay: 2000
});
$('.image-copy-url-button').click(function() { $('.image-copy-url-button').click(function() {
var url = new URL($(this).data('image-path'), location.origin); var url = new URL($(this).data('image-path'), location.origin);
navigator.clipboard.writeText(url); navigator.clipboard.writeText(url);
copy_url_toast.show(); copy_url_toast.show();
}); });
$('.image-copy-md-button').click(function() { $('.image-copy-md-button').click(function() {
var url = new URL($(this).data('image-path'), location.origin); var url = new URL($(this).data('image-path'), location.origin);
navigator.clipboard.writeText('![](' + url + ')'); navigator.clipboard.writeText('![](' + url + ')');
copy_url_toast.show(); copy_url_toast.show();
}); });
</script> </script>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,10 +1,21 @@
<?php requireLib('bootstrap5'); ?>
<?php <?php
$blogs = DB::selectAll("select blogs.id, title, poster, post_time from important_blogs, blogs where is_hidden = 0 and important_blogs.blog_id = blogs.id order by level desc, important_blogs.blog_id desc limit 5"); $blogs = DB::selectAll([
$countdowns = DB::selectAll("select * from countdowns order by end_time asc"); "select blogs.id, title, poster, post_time from important_blogs inner join blogs on important_blogs.blog_id = blogs.id",
$friend_links = DB::selectAll("select * from friend_links order by level desc, id asc"); "where", [
"is_hidden" => 0,
requireLib('bootstrap5'); ], "order by level desc, important_blogs.blog_id desc",
?> DB::limit(5)
]);
$countdowns = DB::selectAll([
"select title, end_time from countdowns",
"order by end_time asc",
]);
$friend_links = DB::selectAll([
"select title, url from friend_links",
"order by level desc, id asc",
]);
?>
<?php echoUOJPageHeader(UOJConfig::$data['profile']['oj-name-short']) ?> <?php echoUOJPageHeader(UOJConfig::$data['profile']['oj-name-short']) ?>
<div class="row"> <div class="row">
<div class="col-lg-9"> <div class="col-lg-9">
@ -21,27 +32,23 @@
<th style="width:20%"></th> <th style="width:20%"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php $now_cnt = 0; ?> <?php $now_cnt = 0; ?>
<?php foreach ($blogs as $blog): ?> <?php foreach ($blogs as $blog_info) : ?>
<?php <?php
$now_cnt++; $blog = new UOJBlog($blog_info);
$new_tag = ''; $now_cnt++;
if ((time() - strtotime($blog['post_time'])) / 3600 / 24 <= 7) {
$new_tag = '<sup style="color:red">&nbsp;new</sup>';
}
?> ?>
<tr> <tr>
<td> <td><?= $blog->getLink(['show_new_tag' => true]) ?></td>
<a href="/blogs/<?= $blog['id'] ?>" class="text-decoration-none"><?= $blog['title'] ?></a> <td>by <?= getUserLink($blog->info['poster']) ?></td>
<?= $new_tag ?> <td><small><?= $blog->info['post_time'] ?></small></td>
</td>
<td>by <?= getUserLink($blog['poster']) ?></td>
<td><small><?= $blog['post_time'] ?></small></td>
</tr> </tr>
<?php endforeach ?> <?php endforeach ?>
<?php for ($i = $now_cnt + 1; $i <= 5; $i++): ?> <?php for ($i = $now_cnt + 1; $i <= 5; $i++) : ?>
<tr><td colspan="233">&nbsp;</td></tr> <tr>
<td colspan="233">&nbsp;</td>
</tr>
<?php endfor ?> <?php endfor ?>
</tbody> </tbody>
</table> </table>
@ -52,27 +59,19 @@
</div> </div>
</div> </div>
</div> </div>
<?php if (!UOJConfig::$data['switch']['force-login'] || Auth::check()): ?> <?php if (Auth::check()) : ?>
<?php if (!UOJConfig::$data['switch']['force-login'] || isNormalUser($myUser)): ?>
<div class="mt-4 card"> <div class="mt-4 card">
<div class="card-body"> <div class="card-body">
<h4 class="card-title"><?= UOJLocale::get('top solver') ?></h4> <h4 class="card-title mb-2"><?= UOJLocale::get('top solver') ?></h4>
<?php echoRanklist([ <?php UOJRanklist::printHTML(['top10' => true]) ?>
'echo_full' => true, <div class="text-center mt-2">
'top10' => true,
'by_accepted' => true,
'table_classes' => ['table', 'text-center'],
]) ?>
<div class="text-center">
<a href="/solverlist" class="text-decoration-none"> <a href="/solverlist" class="text-decoration-none">
<?= UOJLocale::get('view all') ?> <?= UOJLocale::get('view all') ?>
</a> </a>
</div> </div>
</div> </div>
</div> </div>
<?php endif ?> <?php else : ?>
<?php else: ?>
<div class="mt-4 card card-default"> <div class="mt-4 card card-default">
<div class="card-body text-center"> <div class="card-body text-center">
<a role="button" class="btn btn-outline-primary" href="<?= HTML::url('/login') ?>">登录</a> 以查看更多内容。 <a role="button" class="btn btn-outline-primary" href="<?= HTML::url('/login') ?>">登录</a> 以查看更多内容。
@ -90,22 +89,22 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<ul class="list-unstyled mb-0"> <ul class="list-unstyled mb-0">
<?php foreach ($countdowns as $countdown): ?> <?php foreach ($countdowns as $countdown) : ?>
<?php <?php
$enddate = strtotime($countdown['end_time']); $enddate = strtotime($countdown['end_time']);
$nowdate = time(); $nowdate = time();
$diff = ceil(($enddate - $nowdate) / (24 * 60 * 60)); $diff = ceil(($enddate - $nowdate) / (24 * 60 * 60));
?> ?>
<li> <li>
<?php if ($diff > 0): ?> <?php if ($diff > 0) : ?>
<?= UOJLocale::get('x days until countdown title', $countdown['title'], $diff) ?> <?= UOJLocale::get('x days until countdown title', $countdown['title'], $diff) ?>
<?php else: ?> <?php else : ?>
<?= UOJLocale::get("countdown title has begun", $countdown['title']) ?> <?= UOJLocale::get("countdown title has begun", $countdown['title']) ?>
<?php endif ?> <?php endif ?>
</li> </li>
<?php endforeach ?> <?php endforeach ?>
</ul> </ul>
<?php if (count($countdowns) == 0): ?> <?php if (count($countdowns) == 0) : ?>
<div class="text-center"> <div class="text-center">
<?= UOJLocale::get('none') ?> <?= UOJLocale::get('none') ?>
</div> </div>
@ -113,7 +112,7 @@
</div> </div>
</div> </div>
<?php if (Auth::check()): ?> <?php if (Auth::check()) : ?>
<?php uojIncludeView('sidebar', ['assignments_hidden' => '', 'groups_hidden' => '']) ?> <?php uojIncludeView('sidebar', ['assignments_hidden' => '', 'groups_hidden' => '']) ?>
<?php endif ?> <?php endif ?>
@ -123,15 +122,15 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<ul class="ps-3 mb-0"> <ul class="ps-3 mb-0">
<?php foreach ($friend_links as $friend_link): ?> <?php foreach ($friend_links as $friend_link) : ?>
<li> <li>
<a class="text-decoration-none" href="<?= $friend_link['url'] ?>" target="_blank"> <a class="text-decoration-none" href="<?= $friend_link['url'] ?>" target="_blank">
<?= $friend_link['title'] ?> <?= $friend_link['title'] ?>
</a> </a>
</li> </li>
<?php endforeach ?> <?php endforeach ?>
</ul> </ul>
<?php if (count($friend_links) == 0): ?> <?php if (count($friend_links) == 0) : ?>
<div class="text-center"> <div class="text-center">
<?= UOJLocale::get('none') ?> <?= UOJLocale::get('none') ?>
</div> </div>

View File

@ -1,43 +0,0 @@
<?php
requirePHPLib('judger');
if (!authenticateJudger()) {
become404Page();
}
switch ($_GET['type']) {
case 'submission':
$file_name = UOJContext::storagePath()."/submission/{$_GET['id']}/{$_GET['rand_str_id']}";
$download_name = "submission.zip";
break;
case 'tmp':
$file_name = UOJContext::storagePath()."/tmp/{$_GET['rand_str_id']}";
$download_name = "tmp";
break;
case 'problem':
$id = $_GET['id'];
if (!validateUInt($id) || !($problem = queryProblemBrief($id))) {
become404Page();
}
$file_name = "/var/uoj_data/$id.zip";
$download_name = "$id.zip";
break;
case 'judger':
$file_name = UOJContext::storagePath()."/judge_client.zip";
$download_name = "judge_client.zip";
break;
default:
become404Page();
}
$finfo = finfo_open(FILEINFO_MIME);
$mimetype = finfo_file($finfo, $file_name);
if ($mimetype === false) {
become404Page();
}
finfo_close($finfo);
header("X-Sendfile: $file_name");
header("Content-type: $mimetype");
header("Content-Disposition: attachment; filename=$download_name");
?>

View File

@ -1,212 +1,400 @@
<?php <?php
requirePHPLib('judger'); requirePHPLib('judger');
requirePHPLib('data'); requirePHPLib('data');
if (!authenticateJudger()) { if (!authenticateJudger()) {
become404Page(); UOJResponse::page404();
}
function submissionJudged() {
UOJSubmission::onJudged(UOJRequest::post('id'), UOJRequest::post('result'), UOJRequest::post('judge_time'));
}
function customTestSubmissionJudged() {
$submission = DB::selectFirst([
"select submitter, status, content, result, problem_id from custom_test_submissions",
"where", ['id' => $_POST['id']]
]);
if ($submission == null) {
return;
} }
if ($submission['status'] != 'Judging') {
function submissionJudged() { return;
$submission = DB::selectFirst("select submitter, status, content, result, problem_id from submissions where id = {$_POST['id']}"); }
if ($submission == null) { $result = json_decode($_POST['result'], true);
$result['details'] = uojTextEncode($result['details']);
DB::update([
"update custom_test_submissions",
"set", [
'status' => $result['status'],
'status_details' => '',
'result' => json_encode($result, JSON_UNESCAPED_UNICODE)
], "where", ['id' => $_POST['id']]
]);
}
function hackJudged() {
$result = json_decode($_POST['result'], true);
UOJHack::init($_POST['id']);
UOJHack::cur()->setProblem();
UOJHack::cur()->setSubmission();
if ($result['score']) {
$status = 'Judged, Waiting';
} else {
$status = 'Judged';
}
$ok = DB::update([
"update hacks",
"set", [
'success' => $result['score'],
'status' => $status,
'details' => uojTextEncode($result['details'])
], "where", ['id' => $_POST['id']]
]);
if (!$result['score']) {
return;
}
if (!$ok) {
return;
}
if (!(validateUploadedFile('hack_input') && validateUploadedFile('std_output'))) {
UOJLog::error("hack successfully but received no data. id: {$_POST['id']}");
return;
}
$input = UOJContext::storagePath() . UOJHack::info('input');
$up_in = $_FILES["hack_input"]['tmp_name'];
$up_out = $_FILES["std_output"]['tmp_name'];
if (!UOJHack::cur()->problem->needToReviewHack()) {
$err = dataAddHackPoint(UOJHack::cur()->problem->info, $up_in, $up_out);
if ($err === '') {
unlink($input);
DB::update([
"update hacks",
"set", [
'status' => 'Judged'
], "where", ['id' => $_POST['id']]
]);
return; return;
}
if ($submission['status'] != 'Judging' && $submission['status'] != 'Judged, Judging') {
return;
}
$content = json_decode($submission['content'], true);
if (isset($content['first_test_config'])) {
$result = json_decode($submission['result'], true);
$result['final_result'] = json_decode($_POST['result'], true);
$result['final_result']['details'] = uojTextEncode($result['final_result']['details']);
$esc_result = DB::escape(json_encode($result, JSON_UNESCAPED_UNICODE));
$content['final_test_config'] = $content['config'];
$content['config'] = $content['first_test_config'];
unset($content['first_test_config']);
$esc_content = DB::escape(json_encode($content));
DB::update("update submissions set status = 'Judged', result = '$esc_result', content = '$esc_content' where id = {$_POST['id']}");
} else { } else {
$result = json_decode($_POST['result'], true); UOJLog::error("hack successfully but failed to add an extra test: {$err}");
$result['details'] = uojTextEncode($result['details']); }
$esc_result = DB::escape(json_encode($result, JSON_UNESCAPED_UNICODE)); }
if (isset($result["error"])) { move_uploaded_file($up_in, "{$input}_in");
DB::update("update submissions set status = '{$result['status']}', result_error = '{$result['error']}', result = '$esc_result', score = null, used_time = null, used_memory = null where id = {$_POST['id']}"); move_uploaded_file($up_out, "{$input}_out");
DB::update([
"update hacks",
"set", [
'status' => 'Judged, WaitingM'
], "where", ['id' => $_POST['id']]
]);
}
if (isset($_POST['submit'])) {
if (!validateUInt($_POST['id'])) {
die("Wow! hacker! T_T....");
}
if (isset($_POST['is_hack'])) {
hackJudged();
} elseif (isset($_POST['is_custom_test'])) {
customTestSubmissionJudged();
} else {
submissionJudged();
}
}
if (isset($_POST['update-status'])) {
if (!validateUInt($_POST['id'])) {
die("Wow! hacker! T_T....");
}
$status_details = $_POST['status'];
if (isset($_POST['is_custom_test'])) {
DB::update([
"update custom_test_submissions",
"set", ["status_details" => $status_details],
"where", ["id" => $_POST['id']]
]);
} else {
DB::update([
"update submissions",
"set", ["status_details" => $status_details],
"where", ["id" => $_POST['id']]
]);
}
die();
}
$problem_ban_list = DB::selectAll([
"select id from problems",
"where", [
["assigned_to_judger", "!=", "any"],
["assigned_to_judger", "!=", $_POST['judger_name']]
]
]);
foreach ($problem_ban_list as &$val) {
$val = $val['id'];
}
$assignCond = $problem_ban_list ? [["problem_id", "not in", DB::rawtuple($problem_ban_list)]] : [];
$submission = null;
$hack = null;
function querySubmissionToJudge($status, $set_q) {
global $assignCond;
for ($times = 0; $times < 10; $times++) {
$submission = DB::selectFirst([
"select id from submissions",
"where", array_merge(["status" => $status], $assignCond),
"order by id limit 1"
]);
if (!$submission) {
return null;
}
$ok = DB::transaction(function () use (&$submission, $set_q, $status) {
DB::update([
"update submissions",
"set", $set_q,
"where", [
"id" => $submission['id'],
"status" => $status
]
]);
if (DB::affected_rows() == 1) {
$submission = DB::selectFirst([
"select id, problem_id, content, status, judge_time from submissions",
"where", ["id" => $submission['id']]
]);
return true;
} else { } else {
DB::update("update submissions set status = '{$result['status']}', result_error = null, result = '$esc_result', score = {$result['score']}, used_time = {$result['time']}, used_memory = {$result['memory']} where id = {$_POST['id']}");
}
if (isset($content['final_test_config'])) {
$content['first_test_config'] = $content['config'];
$content['config'] = $content['final_test_config'];
unset($content['final_test_config']);
$esc_content = DB::escape(json_encode($content));
DB::update("update submissions set status = 'Judged, Waiting', content = '$esc_content' where id = ${_POST['id']}");
}
}
DB::update("update submissions set status_details = '' where id = {$_POST['id']}");
updateBestACSubmissions($submission['submitter'], $submission['problem_id']);
}
function customTestSubmissionJudged() {
$submission = DB::selectFirst("select submitter, status, content, result, problem_id from custom_test_submissions where id = {$_POST['id']}");
if ($submission == null) {
return;
}
if ($submission['status'] != 'Judging') {
return;
}
$content = json_decode($submission['content'], true);
$result = json_decode($_POST['result'], true);
$result['details'] = uojTextEncode($result['details']);
$esc_result = DB::escape(json_encode($result, JSON_UNESCAPED_UNICODE));
if (isset($result["error"])) {
DB::update("update custom_test_submissions set status = '{$result['status']}', result = '$esc_result' where id = {$_POST['id']}");
} else {
DB::update("update custom_test_submissions set status = '{$result['status']}', result = '$esc_result' where id = {$_POST['id']}");
}
DB::update("update custom_test_submissions set status_details = '' where id = {$_POST['id']}");
}
function hackJudged() {
$result = json_decode($_POST['result'], true);
$esc_details = DB::escape(uojTextEncode($result['details']));
$ok = DB::update("update hacks set success = {$result['score']}, details = '$esc_details' where id = {$_POST['id']}");
if ($ok) {
list($hack_input) = DB::fetch(DB::query("select input from hacks where id = {$_POST['id']}"), MYSQLI_NUM);
unlink(UOJContext::storagePath().$hack_input);
if ($result['score']) {
list($problem_id) = DB::selectFirst("select problem_id from hacks where id = ${_POST['id']}", MYSQLI_NUM);
if (validateUploadedFile('hack_input') && validateUploadedFile('std_output')) {
dataAddExtraTest(queryProblemBrief($problem_id), $_FILES["hack_input"]["tmp_name"], $_FILES["std_output"]["tmp_name"]);
} else {
error_log("hack successfully but received no data. id: ${_POST['id']}");
}
}
}
}
if (isset($_POST['submit'])) {
if (!validateUInt($_POST['id'])) {
die("Wow! hacker! T_T....");
}
if (isset($_POST['is_hack'])) {
hackJudged();
} elseif (isset($_POST['is_custom_test'])) {
customTestSubmissionJudged();
} else {
submissionJudged();
}
}
if (isset($_POST['update-status'])) {
if (!validateUInt($_POST['id'])) {
die("Wow! hacker! T_T....");
}
$esc_status_details = DB::escape($_POST['status']);
if (isset($_POST['is_custom_test'])) {
DB::update("update custom_test_submissions set status_details = '$esc_status_details' where id = {$_POST['id']}");
} else {
DB::update("update submissions set status_details = '$esc_status_details' where id = {$_POST['id']}");
}
die();
}
$submission = null;
$hack = null;
function querySubmissionToJudge($status, $set_q) {
global $submission;
$submission = DB::selectFirst("select id, problem_id, content from submissions where status = '$status' order by id limit 1");
if ($submission) {
DB::update("update submissions set $set_q where id = {$submission['id']} and status = '$status'");
if (DB::affected_rows() != 1) {
$submission = null;
}
}
}
function queryCustomTestSubmissionToJudge() {
global $submission;
$submission = DB::selectFirst("select id, problem_id, content from custom_test_submissions where judge_time is null order by id limit 1");
if ($submission) {
DB::update("update custom_test_submissions set judge_time = now(), status = 'Judging' where id = {$submission['id']} and judge_time is null");
if (DB::affected_rows() != 1) {
$submission = null;
}
}
if ($submission) {
$submission['is_custom_test'] = '';
}
}
function queryHackToJudge() {
global $hack;
$hack = DB::selectFirst("select id, submission_id, input, input_type from hacks where judge_time is null order by id limit 1");
if ($hack) {
DB::update("update hacks set judge_time = now() where id = {$hack['id']} and judge_time is null");
if (DB::affected_rows() != 1) {
$hack = null;
}
}
}
function findSubmissionToJudge() {
global $submission, $hack;
querySubmissionToJudge('Waiting', "judge_time = now(), status = 'Judging'");
if ($submission) {
return true;
}
queryCustomTestSubmissionToJudge();
if ($submission) {
return true;
}
querySubmissionToJudge('Waiting Rejudge', "judge_time = now(), status = 'Judging'");
if ($submission) {
return true;
}
querySubmissionToJudge('Judged, Waiting', "status = 'Judged, Judging'");
if ($submission) {
return true;
}
queryHackToJudge();
if ($hack) {
$submission = DB::selectFirst("select id, problem_id, content from submissions where id = {$hack['submission_id']} and score = 100");
if (!$submission) {
$details = "<error>the score gained by the hacked submission is not 100.\n</error>";
$esc_details = DB::escape(uojTextEncode($details));
DB::update("update hacks set success = 0, details = '$esc_details' where id = {$hack['id']}");
return false; return false;
} }
return true; });
if ($ok) {
return $submission;
} }
return false;
} }
}
function queryMinorSubmissionToJudge($status, $set_q) {
global $assignCond;
if (isset($_POST['fetch_new']) && !$_POST['fetch_new']) {
die("Nothing to judge"); for ($times = 0; $times < 10; $times++) {
$submission = null;
$his = DB::selectFirst([
"select id, submission_id from submissions_history",
"where", ["status" => $status, "major" => 0], // $assignCond is removed!!! fix this bug in the future!
"order by id limit 1"
]);
if (!$his) {
return null;
}
$ok = DB::transaction(function () use (&$submission, &$his, $set_q, $status) {
$submission = DB::selectFirst([
"select id, problem_id, content from submissions",
"where", ["id" => $his['submission_id']], DB::for_share()
]);
if (!$submission) {
return false;
}
DB::update([
"update submissions_history",
"set", $set_q,
"where", [
"id" => $his['id'],
"status" => $status
]
]);
if (DB::affected_rows() == 1) {
$ret = DB::selectFirst([
"select status, judge_time from submissions_history",
"where", ["id" => $his['id']]
]);
if ($ret === false) {
return false;
}
$submission += $ret;
return true;
} else {
return false;
}
});
if ($ok) {
return $submission;
}
} }
if (!findSubmissionToJudge()) { }
die("Nothing to judge"); function queryCustomTestSubmissionToJudge() {
global $assignCond;
while (true) {
$submission = DB::selectFirst([
"select id, problem_id, content from custom_test_submissions",
"where", array_merge(["judge_time" => null], $assignCond),
"order by id limit 1"
]);
if (!$submission) {
return null;
}
$submission['is_custom_test'] = '';
DB::update([
"update custom_test_submissions",
"set", [
"judge_time" => DB::now(),
"status" => 'Judging'
], "where", [
"id" => $submission['id'],
"judge_time" => null
]
]);
if (DB::affected_rows() == 1) {
$submission['status'] = 'Judging';
return $submission;
}
} }
}
$submission['id'] = (int)$submission['id']; function queryHackToJudge() {
$submission['problem_id'] = (int)$submission['problem_id']; global $assignCond;
$submission['problem_mtime'] = filemtime("/var/uoj_data/{$submission['problem_id']}");
$submission['content'] = json_decode($submission['content']); while (true) {
if (DB::selectFirst([
"select 1 from hacks",
"where", [
["status", "!=", "Waiting"],
["status", "!=", "Judged"],
], "order by id limit 1"
])) {
return null;
}
$hack = DB::selectFirst([
"select id, submission_id, input, input_type from hacks",
"where", array_merge(["judge_time" => null], $assignCond),
"order by id limit 1"
]);
if (!$hack) {
return null;
}
DB::update([
"update hacks",
"set", [
"judge_time" => DB::now(),
"status" => 'Judging'
],
"where", [
"id" => $hack['id'],
"judge_time" => null
]
]);
if (DB::affected_rows() == 1) {
$hack['status'] = 'Judging';
return $hack;
}
}
}
function findSubmissionToJudge() {
global $submission, $hack;
$submission = querySubmissionToJudge('Waiting', [
"judge_time" => DB::now(),
"judger" => $_POST['judger_name'],
"status" => 'Judging'
]);
if ($submission) {
return true;
}
$submission = queryCustomTestSubmissionToJudge();
if ($submission) {
return true;
}
$submission = querySubmissionToJudge('Waiting Rejudge', [
"judge_time" => DB::now(),
"judger" => $_POST['judger_name'],
"status" => 'Judging'
]);
if ($submission) {
return true;
}
$submission = querySubmissionToJudge('Judged, Waiting', [
"status" => 'Judged, Judging'
]);
if ($submission) {
return true;
}
$submission = queryMinorSubmissionToJudge('Waiting Rejudge', [
"judge_time" => DB::now(),
"judger" => $_POST['judger_name'],
"status" => 'Judging'
]);
if ($submission) {
return true;
}
$submission = queryMinorSubmissionToJudge('Judged, Waiting', [
"status" => 'Judged, Judging'
]);
if ($submission) {
return true;
}
$hack = queryHackToJudge();
if ($hack) { if ($hack) {
$submission['is_hack'] = ""; $submission = DB::selectFirst([
$submission['hack']['id'] = (int)$hack['id']; "select id, problem_id, content from submissions",
$submission['hack']['input'] = $hack['input']; "where", [
$submission['hack']['input_type'] = $hack['input_type']; "id" => $hack['submission_id'],
"score" => 100
]
]);
if (!$submission) {
$details = "<error>the score gained by the hacked submission is not 100.</error>";
DB::update([
"update hacks",
"set", [
'success' => 0,
'status' => 'Judged',
'details' => uojTextEncode($details)
], "where", ["id" => $hack['id']]
]);
return false;
}
return true;
} }
return false;
echo json_encode($submission); }
?>
if (isset($_POST['fetch_new']) && !$_POST['fetch_new']) {
die("Nothing to judge");
}
if (!findSubmissionToJudge()) {
die("Nothing to judge");
}
$submission['id'] = (int)$submission['id'];
$submission['problem_id'] = (int)$submission['problem_id'];
$submission['problem_mtime'] = filemtime("/var/uoj_data/{$submission['problem_id']}");
$submission['content'] = json_decode($submission['content'], true);
if (isset($submission['status']) && $submission['status'] == 'Judged, Judging' && isset($submission['content']['final_test_config'])) {
$submission['content']['config'] = $submission['content']['final_test_config'];
unset($submission['content']['final_test_config']);
}
if ($hack) {
$submission['is_hack'] = "";
$submission['hack']['id'] = (int)$hack['id'];
$submission['hack']['input'] = $hack['input'];
$submission['hack']['input_type'] = $hack['input_type'];
}
echo json_encode($submission);

View File

@ -1,21 +1,28 @@
<?php <?php
requirePHPLib('judger'); requirePHPLib('judger');
requirePHPLib('data');
if (!authenticateJudger()) {
become404Page(); if (!authenticateJudger()) {
UOJResponse::page404();
}
$res = DB::selectAll([
"select * from judger_info",
"where", [
["ip", "!=", ""],
"enabled" => true
]
]);
foreach ($res as $judger) {
$socket = fsockopen($judger['ip'], UOJConfig::$data['judger']['socket']['port']);
if ($socket === false) {
die("judge client {$judger['ip']} lost.");
} }
fwrite($socket, json_encode([
foreach (DB::selectAll("select * from judger_info where ip != ''") as $judger) { 'password' => UOJConfig::$data['judger']['socket']['password'],
$socket = fsockopen($judger['ip'], UOJConfig::$data['judger']['socket']['port']); 'cmd' => 'update'
if ($socket === false) { ]));
die("judge client {$judger['ip']} lost."); fclose($socket);
} }
fwrite($socket, json_encode([
'password' => UOJConfig::$data['judger']['socket']['password'], die("ok");
'cmd' => 'update'
]));
fclose($socket);
}
die("ok");
?>

View File

@ -1,12 +1,8 @@
<?php <?php
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { if (!Auth::check()) {
redirectToLogin(); redirectToLogin();
} }
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) {
become403Page();
}
requireLib('bootstrap5'); requireLib('bootstrap5');
requireLib('mathjax'); requireLib('mathjax');
requirePHPLib('form'); requirePHPLib('form');
@ -73,7 +69,7 @@ EOD;
echo "<td>{$extra_config['difficulty']}</td>"; echo "<td>{$extra_config['difficulty']}</td>";
} }
} }
echo '<td class="text-start">', getClickZanBlock('P', $problem['id'], $problem['zan'], null, false), '</td>'; echo '<td class="text-start">', ClickZans::getBlock('P', $problem['id'], $problem['zan'], null, false), '</td>';
echo '</tr>'; echo '</tr>';
} }
} }
@ -121,7 +117,7 @@ EOD;
<!-- title container --> <!-- title container -->
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<h1 class="h2"> <h1>
<?= $list['title'] ?> <?= $list['title'] ?>
<span class="fs-5">(ID: #<?= $list['id'] ?>)</span> <span class="fs-5">(ID: #<?= $list['id'] ?>)</span>
<?php if ($list['is_hidden']): ?> <?php if ($list['is_hidden']): ?>
@ -146,7 +142,7 @@ EOD;
<!-- description --> <!-- description -->
<div class="card my-2"> <div class="card my-2">
<div class="card-body"> <div class="card-body">
<h2 class="h5 mb-3">题单简介</h2> <h2 class="h4 mb-3">题单简介</h2>
<?php $description = HTML::purifier()->purify(HTML::parsedown()->text($list['description'])) ?> <?php $description = HTML::purifier()->purify(HTML::parsedown()->text($list['description'])) ?>
<?php if ($description): ?> <?php if ($description): ?>
<?= $description ?> <?= $description ?>
@ -224,7 +220,7 @@ $('#input-show_difficulty').click(function() {
</thead> </thead>
<tbody> <tbody>
<?php foreach ($pag->get() as $idx => $row): ?> <?php foreach ($pag->get() as $idx => $row): ?>
<?php echoProblem($row) ?> <?php echoProblem($row) ?>
<?php endforeach ?> <?php endforeach ?>
<?php if ($pag->isEmpty()): ?> <?php if ($pag->isEmpty()): ?>
<tr> <tr>

View File

@ -44,7 +44,7 @@
if ($cur_tab == 'profile') { if ($cur_tab == 'profile') {
$list_tags = queryProblemListTags($list_id); $list_tags = queryProblemListTags($list_id);
$update_profile_form = new UOJForm('update_profile'); $update_profile_form = new UOJBs4Form('update_profile');
$update_profile_form->addVInput('name', 'text', '标题', $list['title'], $update_profile_form->addVInput('name', 'text', '标题', $list['title'],
function($title, &$vdata) { function($title, &$vdata) {
if ($title == '') { if ($title == '') {
@ -151,7 +151,7 @@ function(res) {
$(window).scrollTop(0); $(window).scrollTop(0);
} }
EOD); EOD);
$update_profile_form->submit_button_config['margin_class'] = 'mt-3'; $update_profile_form->submit_button_config['class_str'] = 'btn btn-secondary mt-3';
$update_profile_form->submit_button_config['text'] = '更新'; $update_profile_form->submit_button_config['text'] = '更新';
$update_profile_form->runAtServer(); $update_profile_form->runAtServer();
} elseif ($cur_tab == 'problems') { } elseif ($cur_tab == 'problems') {
@ -179,7 +179,7 @@ EOD);
$n_problems = DB::selectCount("SELECT count(*) FROM `lists_problems` WHERE `list_id` = {$list['id']}"); $n_problems = DB::selectCount("SELECT count(*) FROM `lists_problems` WHERE `list_id` = {$list['id']}");
$add_new_problem_form = new UOJForm('add_new_problem'); $add_new_problem_form = new UOJBs4Form('add_new_problem');
$add_new_problem_form->addVInput('problem_id', 'text', '题目 ID', '', $add_new_problem_form->addVInput('problem_id', 'text', '题目 ID', '',
function ($problem_id, &$vdata) use ($list) { function ($problem_id, &$vdata) use ($list) {
if (!validateUInt($problem_id)) { if (!validateUInt($problem_id)) {
@ -200,7 +200,7 @@ EOD);
}, },
null null
); );
$add_new_problem_form->submit_button_config['margin_class'] = 'mt-3'; $add_new_problem_form->submit_button_config['class_str'] = 'btn btn-secondary mt-3';
$add_new_problem_form->submit_button_config['text'] = '添加'; $add_new_problem_form->submit_button_config['text'] = '添加';
$add_new_problem_form->handle = function($vdata) use ($list) { $add_new_problem_form->handle = function($vdata) use ($list) {
DB::insert("INSERT INTO `lists_problems` (`list_id`, `problem_id`) values ({$list['id']}, {$vdata['problem_id']})"); DB::insert("INSERT INTO `lists_problems` (`list_id`, `problem_id`) values ({$list['id']}, {$vdata['problem_id']})");
@ -250,7 +250,7 @@ EOD);
dieWithAlert('移除成功!'); dieWithAlert('移除成功!');
} }
$add_new_assignment_form = new UOJForm('add_new_assignment'); $add_new_assignment_form = new UOJBs4Form('add_new_assignment');
$add_new_assignment_form->addVInput('new_assignment_group_id', 'text', '小组 ID', '', $add_new_assignment_form->addVInput('new_assignment_group_id', 'text', '小组 ID', '',
function ($group_id, &$vdata) use ($list) { function ($group_id, &$vdata) use ($list) {
if (!validateUInt($group_id)) { if (!validateUInt($group_id)) {
@ -300,7 +300,7 @@ EOD);
'message' => '题单 #' . $list['id'] . ' 已经被添加到小组 #' . $vdata['group_id'] . ' 的作业列表中,结束时间为 ' . $vdata['end_time']->format('Y-m-d H:i:s') . '。' 'message' => '题单 #' . $list['id'] . ' 已经被添加到小组 #' . $vdata['group_id'] . ' 的作业列表中,结束时间为 ' . $vdata['end_time']->format('Y-m-d H:i:s') . '。'
]); ]);
}; };
$add_new_assignment_form->submit_button_config['margin_class'] = 'mt-3'; $add_new_assignment_form->submit_button_config['class_str'] = 'btn btn-secondary mt-3';
$add_new_assignment_form->submit_button_config['text'] = '添加'; $add_new_assignment_form->submit_button_config['text'] = '添加';
$add_new_assignment_form->setAjaxSubmit(<<<EOD $add_new_assignment_form->setAjaxSubmit(<<<EOD
function(res) { function(res) {
@ -330,7 +330,7 @@ EOD);
<?php echoUOJPageHeader('管理 - ' . $list['title']) ?> <?php echoUOJPageHeader('管理 - ' . $list['title']) ?>
<h1 class="h2"> <h1>
<?= $list['title'] ?> <?= $list['title'] ?>
<small class="fs-5">(ID: #<?= $list['id'] ?>)</small> <small class="fs-5">(ID: #<?= $list['id'] ?>)</small>
管理 管理

View File

@ -1,19 +1,15 @@
<?php <?php
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { if (!Auth::check()) {
redirectToLogin(); redirectToLogin();
} }
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) {
become403Page();
}
requireLib('bootstrap5'); requireLib('bootstrap5');
requirePHPLib('form'); requirePHPLib('form');
requirePHPLib('judger'); requirePHPLib('judger');
requirePHPLib('data'); requirePHPLib('data');
if (isSuperUser($myUser)) { if (isSuperUser($myUser)) {
$new_list_form = new UOJForm('new_list'); $new_list_form = new UOJBs4Form('new_list');
$new_list_form->handle = function() { $new_list_form->handle = function() {
DB::query("insert into lists (title, is_hidden) values ('未命名题单', 1)"); DB::query("insert into lists (title, is_hidden) values ('未命名题单', 1)");
}; };
@ -66,7 +62,7 @@
<!-- title container --> <!-- title container -->
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<h1 class="h2"> <h1>
<?= UOJLocale::get('problems lists') ?> <?= UOJLocale::get('problems lists') ?>
</h1> </h1>
@ -147,7 +143,6 @@ EOD;
'page_len' => 40, 'page_len' => 40,
'table_classes' => ['table', 'table-bordered', 'table-hover', 'table-striped'], 'table_classes' => ['table', 'table-bordered', 'table-hover', 'table-striped'],
'head_pagination' => true, 'head_pagination' => true,
'pagination_table' => 'lists',
'div_classes' => ['card', 'my-3'], 'div_classes' => ['card', 'my-3'],
'table_classes' => ['table', 'uoj-table', 'mb-0'], 'table_classes' => ['table', 'uoj-table', 'mb-0'],
] ]

View File

@ -1,58 +1,57 @@
<?php <?php
use Gregwar\Captcha\PhraseBuilder;
use Gregwar\Captcha\CaptchaBuilder;
requireLib('md5');
requireLib('bootstrap5');
if (Auth::check()) { use Gregwar\Captcha\PhraseBuilder;
redirectTo('/');
requireLib('md5');
requireLib('bootstrap5');
Auth::check() && redirectTo('/');
function handleLoginPost() {
if (!crsf_check()) {
return 'expired';
}
if (!isset($_POST['username'])) {
return "failed";
}
if (!isset($_POST['password'])) {
return "failed";
}
$username = $_POST['username'];
$password = $_POST['password'];
$captcha = $_POST['captcha'];
if (!isset($_SESSION['phrase']) || !PhraseBuilder::comparePhrases($_SESSION['phrase'], $captcha)) {
return "bad_captcha";
} }
function handleLoginPost() { if (!validateUsername($username)) {
if (!crsf_check()) { return "failed";
return 'expired'; }
} if (!validatePassword($password)) {
if (!isset($_POST['username'])) { return "failed";
return "failed"; }
}
if (!isset($_POST['password'])) {
return "failed";
}
$username = $_POST['username'];
$password = $_POST['password'];
$captcha = $_POST['captcha'];
if (!isset($_SESSION['phrase']) || !PhraseBuilder::comparePhrases($_SESSION['phrase'], $captcha)) { $user = UOJUser::query($username);
return "bad_captcha"; if (!$user || !checkPassword($user, $password)) {
} return "failed";
if (!validateUsername($username)) {
return "failed";
}
if (!validatePassword($password)) {
return "failed";
}
$user = queryUser($username);
if (!$user || !checkPassword($user, $password)) {
return "failed";
}
if ($user['usergroup'] == 'B') {
return "banned";
}
Auth::login($user['username']);
return "ok";
} }
if (isset($_POST['login'])) { $account_status = UOJUser::getAccountStatus($user);
echo handleLoginPost(); if ($account_status != 'ok') {
unset($_SESSION['phrase']); return 'account:' . $account_status;
die();
} }
?>
Auth::login($user['username']);
return "ok";
}
if (isset($_POST['login'])) {
echo handleLoginPost();
unset($_SESSION['phrase']);
die();
}
?>
<?php echoUOJPageHeader(UOJLocale::get('login')) ?> <?php echoUOJPageHeader(UOJLocale::get('login')) ?>
<style> <style>
@ -107,79 +106,78 @@
</main> </main>
<script type="text/javascript"> <script type="text/javascript">
function validateLoginPost() { function validateLoginPost() {
var ok = true; var ok = true;
ok &= getFormErrorAndShowHelp('username', validateUsername); ok &= getFormErrorAndShowHelp('username', validateUsername);
ok &= getFormErrorAndShowHelp('password', validatePassword); ok &= getFormErrorAndShowHelp('password', validatePassword);
return ok; return ok;
}
function refreshCaptcha() {
var timestamp = new Date().getTime();
$("#captcha").attr("src", "/captcha" + '?' + timestamp);
}
function submitLoginPost() {
if (!validateLoginPost()) {
return false;
} }
$.post('/login', {
_token : "<?= crsf_token() ?>",
login : '',
username : $('#input-username').val(),
password : md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>"),
captcha: $('#input-captcha').val(),
}, function(msg) {
$('#div-username, #div-password, #div-captcha').removeClass('has-validation');
$('#input-username, #input-password, #input-captcha').removeClass('is-invalid');
$('#help-username, #help-passwor, #help-captcha').html('');
if (msg == 'ok') { function refreshCaptcha() {
var prevUrl = document.referrer; var timestamp = new Date().getTime();
if (prevUrl == '' || /.*\/login.*/.test(prevUrl) || /.*\/logout.*/.test(prevUrl) || /.*\/register.*/.test(prevUrl) || /.*\/reset-password.*/.test(prevUrl)) { $("#captcha").attr("src", "/captcha" + '?' + timestamp);
prevUrl = '/'; }
};
window.location.href = prevUrl; function submitLoginPost() {
} else if (msg == 'bad_captcha') { if (!validateLoginPost()) {
$('#div-captcha').addClass('has-validation'); return false;
$('#div-captcha > .form-floating, #input-captcha').addClass('is-invalid');
$('#help-captcha').html('验证码错误。');
refreshCaptcha();
} else if (msg == 'banned') {
$('#div-username').addClass('has-validation');
$('#div-username > .form-floating, #input-username').addClass('is-invalid');
$('#help-username').html('该用户已被封停,请联系管理员。');
refreshCaptcha();
} else if (msg == 'expired') {
$('#div-username').addClass('has-validation');
$('#div-username > .form-floating, #input-username').addClass('is-invalid');
$('#help-username').html('页面会话已过期。');
refreshCaptcha();
} else {
$('#div-username').addClass('has-validation');
$('#div-username > .form-floating, #input-username').addClass('is-invalid');
$('#div-password').addClass('has-validation');
$('#div-password > .form-floating, #input-password').addClass('is-invalid');
$('#help-password').html('用户名或密码错误。<a href="/forgot-password">忘记密码?</a>');
refreshCaptcha();
} }
});
return true;
}
$(document).ready(function() { $.post('/login', {
refreshCaptcha(); _token: "<?= crsf_token() ?>",
login: '',
username: $('#input-username').val(),
password: md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>"),
captcha: $('#input-captcha').val(),
}, function(msg) {
$('#div-username, #div-password, #div-captcha').removeClass('has-validation');
$('#input-username, #input-password, #input-captcha').removeClass('is-invalid');
$('#help-username, #help-passwor, #help-captcha').html('');
$('#form-login').submit(function(e) { if (msg == 'ok') {
e.preventDefault(); var prevUrl = document.referrer;
submitLoginPost(); if (prevUrl == '' || /.*\/login.*/.test(prevUrl) || /.*\/logout.*/.test(prevUrl) || /.*\/register.*/.test(prevUrl) || /.*\/reset-password.*/.test(prevUrl)) {
}); prevUrl = '/';
$("#captcha").click(function(e) { };
window.location.href = prevUrl;
} else if (msg == 'bad_captcha') {
$('#div-captcha').addClass('has-validation');
$('#div-captcha > .form-floating, #input-captcha').addClass('is-invalid');
$('#help-captcha').html('验证码错误。');
refreshCaptcha();
} else if (msg == 'banned') {
$('#div-username').addClass('has-validation');
$('#div-username > .form-floating, #input-username').addClass('is-invalid');
$('#help-username').html('该用户已被封停,请联系管理员。');
refreshCaptcha();
} else if (msg == 'expired') {
$('#div-username').addClass('has-validation');
$('#div-username > .form-floating, #input-username').addClass('is-invalid');
$('#help-username').html('页面会话已过期。');
refreshCaptcha();
} else {
$('#div-username').addClass('has-validation');
$('#div-username > .form-floating, #input-username').addClass('is-invalid');
$('#div-password').addClass('has-validation');
$('#div-password > .form-floating, #input-password').addClass('is-invalid');
$('#help-password').html('用户名或密码错误。<a href="/forgot-password">忘记密码?</a>');
refreshCaptcha();
}
});
return true;
}
$(document).ready(function() {
refreshCaptcha(); refreshCaptcha();
});
});
$('#form-login').submit(function(e) {
e.preventDefault();
submitLoginPost();
});
$("#captcha").click(function(e) {
refreshCaptcha();
});
});
</script> </script>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,12 +1,12 @@
<?php <?php
crsf_defend(); crsf_defend();
Auth::logout(); Auth::logout();
?> ?>
<script type="text/javascript"> <script type="text/javascript">
var prevUrl = document.referrer; var prevUrl = document.referrer;
if (!prevUrl) { if (!prevUrl) {
prevUrl = '/'; prevUrl = '/';
}; };
window.location.href = prevUrl; window.location.href = prevUrl;
</script> </script>

View File

@ -1,482 +1,431 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requireLib('hljs'); requireLib('hljs');
requireLib('mathjax'); requireLib('mathjax');
requirePHPLib('form'); requirePHPLib('form');
requirePHPLib('judger'); requirePHPLib('judger');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin(); UOJProblem::init(UOJRequest::get('id')) || UOJResponse::page404();
}
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) { $problem = UOJProblem::cur()->info;
become404Page(); $problem_content = UOJProblem::cur()->queryContent();
}
$problem_content = queryProblemContent($problem['id']); if (UOJRequest::get('contest_id')) {
UOJContest::init(UOJRequest::get('contest_id')) || UOJResponse::page404();
$contest = validateUInt($_GET['contest_id']) ? queryContest($_GET['contest_id']) : null; UOJProblem::upgradeToContestProblem() || UOJResponse::page404();
if ($contest != null) { }
genMoreContestInfo($contest); UOJProblem::cur()->userCanView(Auth::user(), ['ensure' => true]);
$problem_rank = queryContestProblemRank($contest, $problem);
if ($problem_rank == null) { $pre_submit_check_ret = UOJProblem::cur()->preSubmitCheck();
become404Page();
} else { $is_participating = false;
$problem_letter = chr(ord('A') + $problem_rank - 1); $no_more_submission = false;
$submission_warning = null;
if (UOJContest::cur()) {
if (UOJContest::cur()->userCanParticipateNow(Auth::user())) {
if (!UOJContest::cur()->userHasMarkedParticipated(Auth::user())) {
redirectTo(UOJContest::cur()->getUri("/confirm"));
} }
} $is_participating = true;
$submit_time_limit = UOJContestProblem::cur()->submitTimeLimit();
$is_in_contest = false; $max_cnt = UOJContest::cur()->maxSubmissionCountPerProblem();
$ban_in_contest = false; if ($submit_time_limit != -1) {
if ($contest != null) { $cur_contest_time = (UOJTime::$time_now->getTimestamp() - UOJContest::info('start_time')->getTimestamp()) / 60;
if (!hasContestPermission($myUser, $contest)) { if ($cur_contest_time > $submit_time_limit) {
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) { $no_more_submission = "本题只能在比赛的前 {$submit_time_limit} 分钟提交,没法再交咯";
become404Page(); }
} elseif ($contest['cur_progress'] == CONTEST_IN_PROGRESS) { }
if (Auth::check()) { if (!$no_more_submission) {
if (hasParticipated($myUser, $contest)) { if ($max_cnt != -1) {
$is_in_contest = true; $cnt = UOJContestProblem::cur()->queryUserSubmissionCountInContest(Auth::user());
} else { if ($cnt >= $max_cnt) {
redirectTo("/contest/{$contest['id']}/confirm"); $no_more_submission = "提交次数已达到 {$cnt} 次,没法再交咯";
}
} else {
redirectToLogin();
} }
}
}
if (!$no_more_submission) {
if ($max_cnt != -1) {
$warning1 = "已使用 {$cnt}/{$max_cnt} 次提交机会";
} else { } else {
$ban_in_contest = !isProblemVisibleToUser($problem, $myUser); $warning1 = null;
if (!hasRegistered($myUser, $contest) && !isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) {
become403Page();
}
} }
} else { if ($submit_time_limit != -1) {
if ($contest['cur_progress'] == CONTEST_IN_PROGRESS) { $warning2 = "注意本题只能在比赛的前 {$submit_time_limit} 分钟提交";
if (hasRegistered($myUser, $contest)) { } else {
if (hasParticipated($myUser, $contest)) { $warning2 = null;
$is_in_contest = true; }
} else { if ($warning1 && $warning2) {
redirectTo("/contest/{$contest['id']}/confirm"); $submission_warning = "{$warning1}{$warning2}";
} } else {
} $submission_warning = $warning1 !== null ? $warning1 : $warning2;
} }
}
} else {
if (!isProblemVisibleToUser($problem, $myUser)) {
become404Page();
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) {
become403Page();
} }
} }
$submission_requirement = json_decode($problem['submission_requirement'], true); // 比赛导航
$problem_extra_config = getProblemExtraConfig($problem); $tabs_info = [
$custom_test_requirement = getProblemCustomTestRequirement($problem); 'dashboard' => [
'name' => UOJLocale::get('contests::contest dashboard'),
'url' => '/contest/' . UOJContest::info('id'),
],
'submissions' => [
'name' => UOJLocale::get('contests::contest submissions'),
'url' => '/contest/' . UOJContest::info('id') . '/submissions',
],
'standings' => [
'name' => UOJLocale::get('contests::contest standings'),
'url' => '/contest/' . UOJContest::info('id') . '/standings',
],
];
if ($custom_test_requirement && Auth::check()) { if (UOJContest::cur()->progress() > CONTEST_TESTING) {
$custom_test_submission = DB::selectFirst("select * from custom_test_submissions where submitter = '".Auth::id()."' and problem_id = {$problem['id']} order by id desc limit 1"); $tabs_info['after_contest_standings'] = [
$custom_test_submission_result = json_decode($custom_test_submission['result'], true); 'name' => UOJLocale::get('contests::after contest standings'),
'url' => '/contest/' . UOJContest::info('id') . '/after_contest_standings',
];
$tabs_info['self_reviews'] = [
'name' => UOJLocale::get('contests::contest self reviews'),
'url' => '/contest/' . UOJContest::info('id') . '/self_reviews',
];
} }
if ($custom_test_requirement && $_GET['get'] == 'custom-test-status-details' && Auth::check()) {
if ($custom_test_submission == null) { if (UOJContest::cur()->userCanManage(Auth::user())) {
$tabs_info['backstage'] = [
'name' => UOJLocale::get('contests::contest backstage'),
'url' => '/contest/' . UOJContest::info('id') . '/backstage',
];
}
}
$submission_requirement = UOJProblem::cur()->getSubmissionRequirement();
$custom_test_requirement = UOJProblem::cur()->getCustomTestRequirement();
$custom_test_enabled = $custom_test_requirement && $pre_submit_check_ret === true;
function handleUpload($zip_file_name, $content, $tot_size) {
global $is_participating;
UOJSubmission::onUpload($zip_file_name, $content, $tot_size, $is_participating);
}
function handleCustomTestUpload($zip_file_name, $content, $tot_size) {
UOJCustomTestSubmission::onUpload($zip_file_name, $content, $tot_size);
}
if ($custom_test_enabled) {
UOJCustomTestSubmission::init(UOJProblem::cur(), Auth::user());
if (UOJRequest::get('get') == 'custom-test-status-details') {
if (!UOJCustomTestSubmission::cur()) {
echo json_encode(null); echo json_encode(null);
} elseif ($custom_test_submission['status'] != 'Judged') { } elseif (!UOJCustomTestSubmission::cur()->hasJudged()) {
echo json_encode(array( echo json_encode([
'judged' => false, 'judged' => false,
'html' => getSubmissionStatusDetails($custom_test_submission) 'waiting' => true,
)); 'html' => UOJCustomTestSubmission::cur()->getStatusDetailsHTML(),
]);
} else { } else {
ob_start(); ob_start();
$styler = new CustomTestSubmissionDetailsStyler(); $styler = new CustomTestSubmissionDetailsStyler();
if (!hasViewPermission($problem_extra_config['view_details_type'], $myUser, $problem, $submission)) { if (!UOJCustomTestSubmission::cur()->userPermissionCodeCheck(Auth::user(), UOJProblem::cur()->getExtraConfig('view_details_type'))) {
$styler->fade_all_details = true; $styler->fade_all_details = true;
} }
echoJudgementDetails($custom_test_submission_result['details'], $styler, 'custom_test_details'); echoJudgmentDetails(UOJCustomTestSubmission::cur()->getResult('details'), $styler, 'custom_test_details');
$result = ob_get_contents(); $result = ob_get_contents();
ob_end_clean(); ob_end_clean();
echo json_encode(array( echo json_encode([
'judged' => true, 'judged' => true,
'html' => getSubmissionStatusDetails($custom_test_submission), 'waiting' => false,
'html' => UOJCustomTestSubmission::cur()->getStatusDetailsHTML(),
'result' => $result 'result' => $result
)); ]);
} }
die(); die();
} }
$can_use_zip_upload = true;
foreach ($submission_requirement as $req) {
if ($req['type'] == 'source code') {
$can_use_zip_upload = false;
}
}
function handleUpload($zip_file_name, $content, $tot_size) {
global $problem, $contest, $myUser, $is_in_contest;
$content['config'][] = array('problem_id', $problem['id']);
if ($is_in_contest && $contest['extra_config']["contest_type"]!='IOI' && !isset($contest['extra_config']["problem_{$problem['id']}"])) {
$content['final_test_config'] = $content['config'];
$content['config'][] = array('test_sample_only', 'on');
}
$esc_content = DB::escape(json_encode($content));
$language = '/'; $custom_test_form = newSubmissionForm(
foreach ($content['config'] as $row) { 'custom_test',
if (strEndWith($row[0], '_language')) { $custom_test_requirement,
$language = $row[1]; 'FS::randomAvailableTmpFileName',
break; 'handleCustomTestUpload'
} );
} $custom_test_form->appendHTML('<div id="div-custom_test_result"></div>');
if ($language != '/') { $custom_test_form->succ_href = 'none';
Cookie::set('uoj_preferred_language', $language, time() + 60 * 60 * 24 * 365, '/'); $custom_test_form->extra_validator = function () {
} if (UOJCustomTestSubmission::cur() && !UOJCustomTestSubmission::cur()->hasJudged()) {
$esc_language = DB::escape($language); return '上一个测评尚未结束';
$result = array();
$result['status'] = "Waiting";
$result_json = json_encode($result);
if ($is_in_contest) {
DB::query("insert into submissions (problem_id, contest_id, submit_time, submitter, content, language, tot_size, status, result, is_hidden) values (${problem['id']}, ${contest['id']}, now(), '${myUser['username']}', '$esc_content', '$esc_language', $tot_size, '${result['status']}', '$result_json', 0)");
} else {
DB::query("insert into submissions (problem_id, submit_time, submitter, content, language, tot_size, status, result, is_hidden) values (${problem['id']}, now(), '${myUser['username']}', '$esc_content', '$esc_language', $tot_size, '${result['status']}', '$result_json', {$problem['is_hidden']})");
}
}
function handleCustomTestUpload($zip_file_name, $content, $tot_size) {
global $problem, $contest, $myUser;
$content['config'][] = array('problem_id', $problem['id']);
$content['config'][] = array('custom_test', 'on');
$esc_content = DB::escape(json_encode($content));
$language = '/';
foreach ($content['config'] as $row) {
if (strEndWith($row[0], '_language')) {
$language = $row[1];
break;
}
}
if ($language != '/') {
Cookie::set('uoj_preferred_language', $language, time() + 60 * 60 * 24 * 365, '/');
}
$esc_language = DB::escape($language);
$result = array();
$result['status'] = "Waiting";
$result_json = json_encode($result);
DB::insert("insert into custom_test_submissions (problem_id, submit_time, submitter, content, status, result) values ({$problem['id']}, now(), '{$myUser['username']}', '$esc_content', '{$result['status']}', '$result_json')");
}
if ($can_use_zip_upload) {
$zip_answer_form = newZipSubmissionForm('zip_answer',
$submission_requirement,
'uojRandAvaiableSubmissionFileName',
'handleUpload');
$zip_answer_form->extra_validator = function() {
global $ban_in_contest;
if ($ban_in_contest) {
return '请耐心等待比赛结束后题目对所有人可见了再提交';
}
return '';
};
$zip_answer_form->succ_href = $is_in_contest ? "/contest/{$contest['id']}/submissions" : '/submissions';
$zip_answer_form->runAtServer();
}
$answer_form = newSubmissionForm('answer',
$submission_requirement,
'uojRandAvaiableSubmissionFileName',
'handleUpload');
$answer_form->extra_validator = function() {
global $ban_in_contest;
if ($ban_in_contest) {
return '请耐心等待比赛结束后题目对所有人可见了再提交';
} }
return ''; return '';
}; };
$answer_form->succ_href = $is_in_contest ? "/contest/{$contest['id']}/submissions" : '/submissions'; $custom_test_form->ctrl_enter_submit = true;
$answer_form->runAtServer(); $custom_test_form->setAjaxSubmit(
<<<EOD
function(response_text) {
custom_test_onsubmit(
response_text,
$('#div-custom_test_result')[0],
'{$_SERVER['REQUEST_URI']}?get=custom-test-status-details'
)
}
EOD
);
$custom_test_form->submit_button_config['text'] = UOJLocale::get('problems::run');
$custom_test_form->runAtServer();
}
if ($custom_test_requirement) { $can_use_zip_upload = true;
$custom_test_form = newSubmissionForm('custom_test', foreach ($submission_requirement as $req) {
$custom_test_requirement, if ($req['type'] == 'source code') {
function() { $can_use_zip_upload = false;
return uojRandAvaiableFileName('/tmp/');
},
'handleCustomTestUpload');
$custom_test_form->appendHTML(<<<EOD
<div id="div-custom_test_result"></div>
EOD
);
$custom_test_form->succ_href = 'none';
$custom_test_form->extra_validator = function() {
global $ban_in_contest, $custom_test_submission;
if ($ban_in_contest) {
return '请耐心等待比赛结束后题目对所有人可见了再提交';
}
if ($custom_test_submission && $custom_test_submission['status'] != 'Judged') {
return '上一个测评尚未结束';
}
return '';
};
$custom_test_form->ctrl_enter_submit = true;
$custom_test_form->setAjaxSubmit(<<<EOD
function(response_text) {custom_test_onsubmit(response_text, $('#div-custom_test_result')[0], '{$_SERVER['REQUEST_URI']}?get=custom-test-status-details')}
EOD
);
$custom_test_form->submit_button_config['text'] = UOJLocale::get('problems::run');
$custom_test_form->runAtServer();
} }
?> }
if ($pre_submit_check_ret === true && !$no_more_submission) {
$submission_extra_validator = function () {
if (!submission_frequency_check()) {
UOJLog::warning('a user exceeds the submission frequency limit! ' . Auth::id() . ' at problem #' . UOJProblem::info('id'));
return '交题交得太快啦,坐下来喝杯阿华田休息下吧?';
}
return '';
};
if (UOJProblem::cur()->userCanUploadSubmissionViaZip(Auth::user())) {
$zip_answer_form = newZipSubmissionForm(
'zip-answer',
$submission_requirement,
'FS::randomAvailableSubmissionFileName',
'handleUpload'
);
$zip_answer_form->extra_validators[] = $submission_extra_validator;
$zip_answer_form->succ_href = $is_participating ? '/contest/' . UOJContest::info('id') . '/submissions' : '/submissions';
$zip_answer_form->runAtServer();
}
$answer_form = newSubmissionForm(
'answer',
$submission_requirement,
'FS::randomAvailableSubmissionFileName',
'handleUpload'
);
$answer_form->extra_validator = $submission_extra_validator;
$answer_form->succ_href = $is_participating ? '/contest/' . UOJContest::info('id') . '/submissions' : '/submissions';
$answer_form->runAtServer();
}
$conf = UOJProblem::cur()->getProblemConf();
?>
<?php echoUOJPageHeader(HTML::stripTags($problem['title']) . ' - ' . UOJLocale::get('problems::problem')) ?> <?php echoUOJPageHeader(HTML::stripTags($problem['title']) . ' - ' . UOJLocale::get('problems::problem')) ?>
<?php
$limit = getUOJConf("/var/uoj_data/{$problem['id']}/problem.conf");
$time_limit = $limit['time_limit'];
$memory_limit = $limit['memory_limit'];
$problem_uploader = $problem['uploader'];
?>
<div class="row"> <div class="row">
<!-- Left col -->
<div class="col-lg-9">
<!-- Left col --> <?php if (isset($tabs_info)) : ?>
<div class="col-lg-9"> <!-- 比赛导航 -->
<div class="mb-2">
<?php if ($contest): ?> <?= HTML::tablist($tabs_info, '', 'nav-pills') ?>
<!-- 比赛导航 --> </div>
<?php
$tabs_info = array(
'dashboard' => array(
'name' => UOJLocale::get('contests::contest dashboard'),
'url' => "/contest/{$contest['id']}"
),
'submissions' => array(
'name' => UOJLocale::get('contests::contest submissions'),
'url' => "/contest/{$contest['id']}/submissions"
),
'standings' => array(
'name' => UOJLocale::get('contests::contest standings'),
'url' => "/contest/{$contest['id']}/standings"
),
);
if ($contest['cur_progress'] > CONTEST_TESTING) {
$tabs_info['after_contest_standings'] = array(
'name' => UOJLocale::get('contests::after contest standings'),
'url' => "/contest/{$contest['id']}/after_contest_standings"
);
$tabs_info['self_reviews'] = array(
'name' => UOJLocale::get('contests::contest self reviews'),
'url' => "/contest/{$contest['id']}/self_reviews"
);
}
if (hasContestPermission(Auth::user(), $contest)) {
$tabs_info['backstage'] = array(
'name' => UOJLocale::get('contests::contest backstage'),
'url' => "/contest/{$contest['id']}/backstage"
);
}
?>
<div class="mb-2">
<?= HTML::tablist($tabs_info, '', 'nav-pills') ?>
</div>
<?php endif ?>
<div class="card card-default mb-2">
<div class="card-body">
<h1 class="h2 card-title text-center">
<?php if ($contest): ?>
<?= $problem_letter ?>. <?= $problem['title'] ?>
<?php else: ?>
#<?= $problem['id']?>. <?= $problem['title'] ?>
<?php endif ?>
</h1>
<div class="text-center small">
时间限制: <?= $time_limit != null ? "$time_limit s" : "N/A" ?>
&emsp;
空间限制: <?= $memory_limit != null ? "$memory_limit MB" : "N/A" ?>
&emsp;
上传者: <?= getUserLink($problem_uploader ?: "root") ?>
</div>
<hr>
<div class="tab-content">
<div class="tab-pane active" id="statement">
<article class="mt-3 markdown-body">
<?= $problem_content['statement'] ?>
</article>
</div>
<div class="tab-pane" id="submit">
<div class="top-buffer-sm"></div>
<?php if ($can_use_zip_upload): ?>
<?php $zip_answer_form->printHTML(); ?>
<hr />
<strong><?= UOJLocale::get('problems::or upload files one by one') ?><br /></strong>
<?php endif ?> <?php endif ?>
<?php $answer_form->printHTML(); ?>
</div>
<?php if ($custom_test_requirement): ?>
<div class="tab-pane" id="custom-test">
<div class="top-buffer-sm"></div>
<?php $custom_test_form->printHTML(); ?>
</div>
<?php endif ?>
</div>
</div> <div class="card card-default mb-2">
</div> <div class="card-body">
</div> <h1 class="card-title text-center">
<!-- End left col --> <?php if (UOJContest::cur()) : ?>
<?= UOJProblem::cur()->getTitle(['with' => 'letter', 'simplify' => true]) ?>
<?php else : ?>
<?= UOJProblem::cur()->getTitle(['with' => 'id']) ?>
<?php endif ?>
</h1>
<aside class="col-lg-3 mt-3 mt-lg-0"> <?php
<!-- Right col --> $time_limit = $conf instanceof UOJProblemConf ? $conf->getVal('time_limit', null) : null;
$memory_limit = $conf instanceof UOJProblemConf ? $conf->getVal('memory_limit', null) : null;
?>
<div class="text-center small">
时间限制: <?= $time_limit ? "$time_limit s" : "N/A" ?>
&emsp;
空间限制: <?= $memory_limit ? "$memory_limit MB" : "N/A" ?>
&emsp;
上传者: <?= UOJProblem::cur()->getUploaderLink() ?>
</div>
<?php if ($contest): ?> <hr>
<!-- Contest card -->
<div class="card card-default mb-2"> <div class="tab-content">
<div class="card-body"> <div class="tab-pane active" id="statement">
<h3 class="h5 card-title text-center"> <article class="mt-3 markdown-body">
<a class="text-decoration-none text-body" href="/contest/<?= $contest['id'] ?>"> <?= $problem_content['statement'] ?>
<?= $contest['name'] ?> </article>
</a> </div>
</h3> <div class="tab-pane" id="submit">
<div class="card-text text-center text-muted"> <?php if ($pre_submit_check_ret !== true) : ?>
<?php if ($contest['cur_progress'] <= CONTEST_IN_PROGRESS): ?> <h3 class="text-warning"><?= $pre_submit_check_ret ?></h3>
<span id="contest-countdown"></span> <?php elseif ($no_more_submission) : ?>
<?php else: ?> <h3 class="text-warning"><?= $no_more_submission ?></h3>
<?= UOJLocale::get('contests::contest ended') ?> <?php else : ?>
<?php endif ?> <?php if ($submission_warning) : ?>
<h3 class="text-warning"><?= $submission_warning ?></h3>
<?php endif ?>
<?php if (isset($zip_answer_form)) : ?>
<?php $zip_answer_form->printHTML(); ?>
<hr />
<strong><?= UOJLocale::get('problems::or upload files one by one') ?><br /></strong>
<?php endif ?>
<?php $answer_form->printHTML(); ?>
<?php endif ?>
</div>
<?php if ($custom_test_enabled) : ?>
<div class="tab-pane" id="custom-test">
<?php $custom_test_form->printHTML(); ?>
</div>
<?php endif ?>
</div>
</div>
</div> </div>
</div>
<div class="card-footer bg-transparent">
比赛评价:<?= getClickZanBlock('C', $contest['id'], $contest['zan']) ?>
</div>
</div>
<?php if ($contest['cur_progress'] <= CONTEST_IN_PROGRESS): ?>
<script type="text/javascript">
$('#contest-countdown').countdown(<?= $contest['end_time']->getTimestamp() - UOJTime::$time_now->getTimestamp() ?>, function(){}, '1.75rem', false);
</script>
<?php endif ?>
<?php endif ?>
<!-- 题目导航卡片 --> </div>
<div class="card card-default mb-2"> <!-- End left col -->
<ul class="nav nav-pills nav-fill flex-column" role="tablist">
<li class="nav-item text-start"> <aside class="col-lg-3 mt-3 mt-lg-0">
<a href="#statement" class="nav-link active" role="tab" data-bs-toggle="pill" data-bs-target="#statement"> <!-- Right col -->
<i class="bi bi-journal-text"></i>
<?= UOJLocale::get('problems::statement') ?> <?php if (UOJContest::cur()) : ?>
</a> <!-- Contest card -->
</li> <div class="card card-default mb-2">
<li class="nav-item text-start"> <div class="card-body">
<a href="#submit" class="nav-link" role="tab" data-bs-toggle="pill" data-bs-target="#submit"> <h3 class="h4 card-title text-center">
<i class="bi bi-upload"></i> <a class="text-decoration-none text-body" href="/contest/<?= UOJContest::info('id') ?>">
<?= UOJLocale::get('problems::submit') ?> <?= UOJContest::info('name') ?>
</a> </a>
</li> </h3>
<?php if ($custom_test_requirement): ?> <div class="card-text text-center text-muted">
<li class="nav-item text-start"> <?php if (UOJContest::cur()->progress() <= CONTEST_IN_PROGRESS) : ?>
<a class="nav-link" href="#custom-test" role="tab" data-bs-toggle="pill" data-bs-target="#custom-test"> <span id="contest-countdown"></span>
<i class="bi bi-braces"></i> <?php else : ?>
<?= UOJLocale::get('problems::custom test') ?> <?= UOJLocale::get('contests::contest ended') ?>
</a> <?php endif ?>
</li> </div>
<?php endif ?> </div>
<?php if (!$contest || $contest['cur_progress'] >= CONTEST_FINISHED): ?> <div class="card-footer bg-transparent">
<li class="nav-item text-start"> 比赛评价:<?= ClickZans::getBlock('C', UOJContest::info('id'), UOJContest::info('zan')) ?>
<a href="/problem/<?= $problem['id'] ?>/solutions" class="nav-link" role="tab"> </div>
<i class="bi bi-journal-bookmark"></i> </div>
<?= UOJLocale::get('problems::solutions') ?> <?php if (UOJContest::cur()->progress() <= CONTEST_IN_PROGRESS) : ?>
</a> <script>
</li> $('#contest-countdown').countdown(<?= UOJContest::info('end_time')->getTimestamp() - UOJTime::$time_now->getTimestamp() ?>, function() {}, '1.75rem', false);
<?php endif ?> </script>
<li class="nav-item text-start">
<a class="nav-link"
<?php if ($contest): ?>
href="/contest/<?= $contest['id'] ?>/problem/<?= $problem['id'] ?>/statistics"
<?php else: ?>
href="/problem/<?= $problem['id'] ?>/statistics"
<?php endif ?> <?php endif ?>
>
<i class="bi bi-graph-up"></i>
<?= UOJLocale::get('problems::statistics') ?>
</a>
</li>
<?php if (hasProblemPermission($myUser, $problem)): ?>
<li class="nav-item text-start">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab">
<i class="bi bi-sliders"></i>
<?= UOJLocale::get('problems::manage') ?>
</a>
</li>
<?php endif ?> <?php endif ?>
</ul>
<div class="card-footer bg-transparent">
评价:<?= getClickZanBlock('P', $problem['id'], $problem['zan']) ?>
</div>
</div>
<!-- 附件 --> <!-- 题目导航卡片 -->
<div class="card card-default mb-2"> <div class="card card-default mb-2">
<ul class="nav nav-fill flex-column"> <ul class="nav nav-pills nav-fill flex-column" role="tablist">
<li class="nav-item text-start"> <li class="nav-item text-start">
<a class="nav-link" href="<?= HTML::url("/download.php?type=problem&id={$problem['id']}") ?>"> <a href="#statement" class="nav-link active" role="tab" data-bs-toggle="pill" data-bs-target="#statement">
<i class="bi bi-hdd-stack"></i> <i class="bi bi-journal-text"></i>
测试数据 <?= UOJLocale::get('problems::statement') ?>
</a> </a>
</li> </li>
<li class="nav-item text-start"> <li class="nav-item text-start">
<a class="nav-link" href="<?= HTML::url("/download.php?type=attachment&id={$problem['id']}") ?>"> <a href="#submit" class="nav-link" role="tab" data-bs-toggle="pill" data-bs-target="#submit">
<i class="bi bi-download"></i> <i class="bi bi-upload"></i>
附件下载 <?= UOJLocale::get('problems::submit') ?>
</a> </a>
</li> </li>
</ul> <?php if ($custom_test_enabled) : ?>
</div> <li class="nav-item text-start">
<a class="nav-link" href="#custom-test" role="tab" data-bs-toggle="pill" data-bs-target="#custom-test">
<i class="bi bi-braces"></i>
<?= UOJLocale::get('problems::custom test') ?>
</a>
</li>
<?php endif ?>
<?php if (!UOJContest::cur() || UOJContest::cur()->progress() >= CONTEST_FINISHED) : ?>
<li class="nav-item text-start">
<a href="/problem/<?= $problem['id'] ?>/solutions" class="nav-link" role="tab">
<i class="bi bi-journal-bookmark"></i>
<?= UOJLocale::get('problems::solutions') ?>
</a>
</li>
<?php endif ?>
<?php if (UOJContest::cur() && UOJContest::cur()->userCanSeeProblemStatistics(Auth::user())) : ?>
<li class="nav-item text-start">
<a class="nav-link" href="/contest/<?= UOJContest::info('id') ?>/problem/<?= $problem['id'] ?>/statistics">
<i class="bi bi-graph-up"></i>
<?= UOJLocale::get('problems::statistics') ?>
</a>
</li>
<?php elseif (!UOJContest::cur()) : ?>
<li class="nav-item text-start">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/statistics">
<i class="bi bi-graph-up"></i>
<?= UOJLocale::get('problems::statistics') ?>
</a>
</li>
<?php endif ?>
<?php if (UOJProblem::cur()->userCanManage(Auth::user())) : ?>
<li class="nav-item text-start">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab">
<i class="bi bi-sliders"></i>
<?= UOJLocale::get('problems::manage') ?>
</a>
</li>
<?php endif ?>
</ul>
<div class="card-footer bg-transparent">
评价:<?= ClickZans::getBlock('P', $problem['id'], $problem['zan']) ?>
</div>
</div>
<?php <!-- 附件 -->
$sidebar_config = array(); <div class="card card-default mb-2">
if ($contest && $contest['cur_progress'] <= CONTEST_IN_PROGRESS) { <ul class="nav nav-fill flex-column">
$sidebar_config['upcoming_contests_hidden'] = ''; <?php if (UOJProblem::cur()->userCanDownloadTestData(Auth::user())) : ?>
} <li class="nav-item text-start">
uojIncludeView('sidebar', $sidebar_config); <a class="nav-link" href="<?= HTML::url("/download/problem/{$problem['id']}/data.zip") ?>">
?> <i class="bi bi-hdd-stack"></i>
</aside> 测试数据
<!-- end right col --> </a>
</li>
<?php endif ?>
<li class="nav-item text-start">
<a class="nav-link" href="<?= HTML::url("/download/problem/{$problem['id']}/attachment.zip") ?>">
<i class="bi bi-download"></i>
附件下载
</a>
</li>
</ul>
</div>
<?php
$sidebar_config = [];
if (UOJContest::cur() && UOJContest::cur()->progress() <= CONTEST_IN_PROGRESS) {
$sidebar_config['upcoming_contests_hidden'] = '';
}
uojIncludeView('sidebar', $sidebar_config);
?>
</aside>
<!-- end right col -->
</div> </div>
<script> <script>
$(document).ready(function() { $(document).ready(function() {
// Javascript to enable link to tab // Javascript to enable link to tab
var hash = location.hash.replace(/^#/, ''); var hash = location.hash.replace(/^#/, '');
if (hash) { if (hash) {
bootstrap.Tab.jQueryInterface.call($('.nav-pills a[href="#' + hash + '"]'), 'show').blur(); bootstrap.Tab.jQueryInterface.call($('.nav-pills a[href="#' + hash + '"]'), 'show').blur();
}
// Change hash for page-reload
$('.nav-pills a').on('shown.bs.tab', function(e) {
if (e.target.hash == '#statement') {
window.location.hash = '';
} else {
window.location.hash = e.target.hash;
} }
});
});
</script>
<?php if ($contest && $contest['cur_progress'] <= CONTEST_IN_PROGRESS): ?> // Change hash for page-reload
<script type="text/javascript"> $('.nav-pills a').on('shown.bs.tab', function(e) {
checkContestNotice(<?= $contest['id'] ?>, '<?= UOJTime::$time_now_str ?>'); if (e.target.hash == '#statement') {
window.location.hash = '';
} else {
window.location.hash = e.target.hash;
}
});
});
</script> </script>
<?php endif ?>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

File diff suppressed because it is too large Load Diff

View File

@ -1,169 +1,171 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requirePHPLib('form'); requirePHPLib('form');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin(); UOJProblem::init(UOJRequest::get('id')) || UOJResponse::page404();
} UOJProblem::cur()->userCanManage(Auth::user()) || UOJResponse::page403();
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) { $managers_form = newAddDelCmdForm(
become404Page(); 'managers',
} 'validateUserAndStoreByUsername',
if (!hasProblemPermission($myUser, $problem)) { function ($type, $username, &$vdata) {
become403Page(); $user = $vdata['user'][$username];
if ($type == '+') {
DB::insert([
"insert into problems_permissions",
"(problem_id, username)",
"values", DB::tuple([UOJProblem::info('id'), $user['username']])
]);
} else if ($type == '-') {
DB::delete([
"delete from problems_permissions",
"where", [
"problem_id" => UOJProblem::info('id'),
"username" => $user['username']
]
]);
}
} }
);
$managers_form = newAddDelCmdForm('managers', $managers_form->runAtServer();
function($username) {
if (!validateUsername($username) || !queryUser($username)) { if (isSuperUser($myUser)) {
return "不存在名为{$username}的用户"; $update_uploader_form = new UOJBs4Form('update_uploader');
$update_uploader_form->addInput(
'new_uploader_username',
'text',
'用户名',
$problem['uploader'] ?: 'root',
function ($username, &$vdata) {
if (!UOJUser::query($username)) {
return '用户不存在';
} }
$vdata['username'] = $username;
return ''; return '';
}, },
function($type, $username) { null
global $problem;
if ($type == '+') {
DB::query("insert into problems_permissions (problem_id, username) values (${problem['id']}, '$username')");
} elseif ($type == '-') {
DB::query("delete from problems_permissions where problem_id = ${problem['id']} and username = '$username'");
}
}
); );
$update_uploader_form->submit_button_config['align'] = 'compressed';
$managers_form->runAtServer(); $update_uploader_form->submit_button_config['text'] = '修改上传者';
$update_uploader_form->submit_button_config['class_str'] = 'mt-2 btn btn-warning';
$update_uploader_form->handle = function (&$vdata) {
if (isSuperUser($myUser)) { DB::update([
$update_uploader_form = new UOJForm('update_uploader'); "update problems",
$update_uploader_form->addInput('new_uploader_username', 'text', '用户名', $problem['uploader'] ?: 'root', "set", ["uploader" => $vdata['username']],
function ($x) { "where", ["id" => UOJProblem::info('id')]
if (!validateUsername($x)) { ]);
return '用户名不合法'; };
} $update_uploader_form->runAtServer();
}
if (!queryUser($x)) { ?>
return '用户不存在'; <?php echoUOJPageHeader('管理者 - ' . HTML::stripTags($problem['title'])) ?>
}
return '';
},
null
);
$update_uploader_form->submit_button_config['align'] = 'compressed';
$update_uploader_form->submit_button_config['text'] = '修改上传者';
$update_uploader_form->submit_button_config['class_str'] = 'mt-2 btn btn-warning';
$update_uploader_form->handle = function() {
global $problem;
$username = $_POST['new_uploader_username'];
DB::query("update problems set uploader = '{$username}' where id = {$problem['id']}");
};
$update_uploader_form->runAtServer();
}
?>
<?php echoUOJPageHeader(HTML::stripTags($problem['title']) . ' - 管理者 - 题目管理') ?>
<div class="row"> <div class="row">
<!-- left col --> <!-- left col -->
<div class="col-lg-9"> <div class="col-lg-9">
<h1>
<?= UOJProblem::cur()->getTitle() ?> 管理
</h1>
<h1 class="h2"> <ul class="nav nav-pills my-3" role="tablist">
#<?= $problem['id'] ?>. <?= $problem['title'] ?> 管理 <li class="nav-item">
</h1> <a class="nav-link" href="/problem/<?= UOJProblem::info('id') ?>/manage/statement" role="tab">
题面
</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/problem/<?= UOJProblem::info('id') ?>/manage/managers" role="tab">
管理者
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/problem/<?= UOJProblem::info('id') ?>/manage/data" role="tab">
数据
</a>
</li>
</ul>
<ul class="nav nav-pills my-3" role="tablist"> <div class="card card-default">
<li class="nav-item"> <div class="card-body">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab">
题面
</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/problem/<?= $problem['id'] ?>/manage/managers" role="tab">
管理者
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/manage/data" role="tab">
数据
</a>
</li>
</ul>
<div class="card card-default"> <table class="table">
<div class="card-body"> <thead>
<tr>
<th>#</th>
<th>用户名</th>
</tr>
</thead>
<tbody>
<?php
$row_id = 0;
$res = DB::selectAll([
"select username from problems_permissions",
"where", ["problem_id" => UOJProblem::info('id')]
]);
foreach ($res as $row) {
$row_id++;
echo '<tr>', '<td>', $row_id, '</td>', '<td>', getUserLink($row['username']), '</td>', '</tr>';
}
?>
</tbody>
</table>
<p class="text-center">命令格式:命令一行一个,+mike表示把mike加入管理者-mike表示把mike从管理者中移除</p>
<?php $managers_form->printHTML(); ?>
<table class="table"> <?php if (isset($update_uploader_form)) : ?>
<thead> <hr>
<tr>
<th>#</th>
<th>用户名</th>
</tr>
</thead>
<tbody>
<?php
$row_id = 0;
$result = DB::query("select username from problems_permissions where problem_id = ${problem['id']}");
while ($row = DB::fetch($result, MYSQLI_ASSOC)) {
$row_id++;
echo '<tr>', '<td>', $row_id, '</td>', '<td>', getUserLink($row['username']), '</td>', '</tr>';
}
?>
</tbody>
</table>
<p class="text-center">命令格式:命令一行一个,+mike表示把mike加入管理者-mike表示把mike从管理者中移除</p>
<?php $managers_form->printHTML(); ?>
<?php if (isset($update_uploader_form)): ?> <?php $update_uploader_form->printHTML(); ?>
<hr> <?php endif ?>
<?php $update_uploader_form->printHTML(); ?> </div>
<?php endif ?> </div>
</div> <!-- end left col -->
</div>
<!-- end left col -->
</div>
<!-- right col -->
<aside class="col-lg-3 mt-3 mt-lg-0">
<div class="card card-default mb-2">
<ul class="nav nav-pills nav-fill flex-column" role="tablist">
<li class="nav-item text-start">
<a href="/problem/<?= $problem['id'] ?>" class="nav-link" role="tab">
<i class="bi bi-journal-text"></i>
<?= UOJLocale::get('problems::statement') ?>
</a>
</li>
<li class="nav-item text-start">
<a href="/problem/<?= $problem['id'] ?>/solutions" class="nav-link" role="tab">
<i class="bi bi-journal-bookmark"></i>
<?= UOJLocale::get('problems::solutions') ?>
</a>
</li>
<li class="nav-item text-start">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/statistics">
<i class="bi bi-graph-up"></i>
<?= UOJLocale::get('problems::statistics') ?>
</a>
</li>
<li class="nav-item text-start">
<a class="nav-link active" href="#" role="tab">
<i class="bi bi-sliders"></i>
<?= UOJLocale::get('problems::manage') ?>
</a>
</li>
</ul>
<div class="card-footer bg-transparent">
评价:<?= getClickZanBlock('P', $problem['id'], $problem['zan']) ?>
</div> </div>
</div>
<?php uojIncludeView('sidebar', array()) ?> <!-- right col -->
</aside> <aside class="col-lg-3 mt-3 mt-lg-0">
<!-- end right col -->
<div class="card card-default mb-2">
<ul class="nav nav-pills nav-fill flex-column" role="tablist">
<li class="nav-item text-start">
<a href="/problem/<?= UOJProblem::info('id') ?>" class="nav-link" role="tab">
<i class="bi bi-journal-text"></i>
<?= UOJLocale::get('problems::statement') ?>
</a>
</li>
<li class="nav-item text-start">
<a href="/problem/<?= UOJProblem::info('id') ?>/solutions" class="nav-link" role="tab">
<i class="bi bi-journal-bookmark"></i>
<?= UOJLocale::get('problems::solutions') ?>
</a>
</li>
<li class="nav-item text-start">
<a class="nav-link" href="/problem/<?= UOJProblem::info('id') ?>/statistics">
<i class="bi bi-graph-up"></i>
<?= UOJLocale::get('problems::statistics') ?>
</a>
</li>
<li class="nav-item text-start">
<a class="nav-link active" href="#" role="tab">
<i class="bi bi-sliders"></i>
<?= UOJLocale::get('problems::manage') ?>
</a>
</li>
</ul>
<div class="card-footer bg-transparent">
评价:<?= ClickZans::getBlock('P', $problem['id'], $problem['zan']) ?>
</div>
</div>
<?php uojIncludeView('sidebar') ?>
</aside>
<!-- end right col -->
</div> </div>

View File

@ -1,321 +1,337 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requirePHPLib('form'); requirePHPLib('form');
requirePHPLib('judger'); requirePHPLib('judger');
requirePHPLib('data'); requirePHPLib('data');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin();
if (isSuperUser($myUser) || isProblemManager($myUser) || isProblemUploader($myUser)) {
$new_problem_form = new UOJBs4Form('new_problem');
$new_problem_form->handle = function () {
DB::insert([
"insert into problems",
"(title, uploader, is_hidden, submission_requirement)",
"values", DB::tuple(["New Problem", Auth::id(), 1, "{}"])
]);
$id = DB::insert_id();
DB::insert([
"insert into problems_contents",
"(id, statement, statement_md)",
"values", DB::tuple([$id, "", "## 题目描述\n\n## 输入格式\n\n## 输出格式\n\n## 输入输出样例\n\n### 输入样例 #1\n\n<!-- 请将样例用代码块包裹起来 -->\n\n### 输出样例 #1\n\n<!-- 请将样例用代码块包裹起来 -->\n\n### 样例解释 #1\n\n<!--\n后续添加样例时格式类似,如果声明大样例的话可以使用这种格式:\n\n### 样例 #2\n\n见右侧「附件下载」中的 `ex_data2.in/out`。\n\n-->\n\n## 数据范围与约定\n\n<!-- 数据范围与一些其他提示 -->\n"])
]);
dataNewProblem($id);
};
$new_problem_form->submit_button_config['align'] = 'right';
$new_problem_form->submit_button_config['class_str'] = 'btn btn-primary';
$new_problem_form->submit_button_config['text'] = UOJLocale::get('problems::add new');
$new_problem_form->submit_button_config['smart_confirm'] = '';
$new_problem_form->runAtServer();
}
function getProblemTR($info) {
$problem = new UOJProblem($info);
$html = '<tr class="text-center">';
if ($info['submission_id']) {
$html .= '<td class="table-success">';
} else {
$html .= '<td>';
} }
$html .= "#{$info['id']}</td>";
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { $html .= '<td class="text-start">';
become403Page(); $html .= $problem->getLink(['with' => 'none']);
if ($problem->isUserOwnProblem(Auth::user())) {
$html .= ' <span class="badge text-white bg-info">' . UOJLocale::get('problems::my problem') . '</span> ';
} }
if ($info['is_hidden']) {
$html .= ' <span class="badge text-bg-danger"><i class="bi bi-eye-slash-fill"></i> ' . UOJLocale::get('hidden') . '</span> ';
if (isSuperUser($myUser) || isProblemManager($myUser) || isProblemUploader($myUser)) {
$new_problem_form = new UOJForm('new_problem');
$new_problem_form->handle = function() {
global $myUser;
DB::query("insert into problems (title, uploader, is_hidden, submission_requirement) values ('New Problem', '{$myUser['username']}', 1, '{}')");
$id = DB::insert_id();
DB::query("insert into problems_contents (id, statement, statement_md) values ($id, '', '')");
dataNewProblem($id);
};
$new_problem_form->submit_button_config['align'] = 'right';
$new_problem_form->submit_button_config['class_str'] = 'btn btn-primary';
$new_problem_form->submit_button_config['text'] = UOJLocale::get('problems::add new');
$new_problem_form->submit_button_config['smart_confirm'] = '';
$new_problem_form->runAtServer();
} }
if (isset($_COOKIE['show_tags_mode'])) {
function echoProblem($problem) { foreach ($problem->queryTags() as $tag) {
global $myUser; $html .= ' <a class="uoj-problem-tag">' . '<span class="badge bg-secondary">' . HTML::escape($tag) . '</span>' . '</a> ';
if (isProblemVisibleToUser($problem, $myUser)) {
echo '<tr class="text-center">';
if ($problem['submission_id']) {
echo '<td class="table-success">';
} else {
echo '<td>';
}
echo '#', $problem['id'], '</td>';
echo '<td class="text-start">';
echo '<a class="text-decoration-none" href="/problem/', $problem['id'], '">', $problem['title'], '</a>';
if ($problem['uploader'] == $myUser['username']) {
echo ' <span class="badge text-white bg-info">', UOJLocale::get('problems::my problem') ,'</span> ';
}
if ($problem['is_hidden']) {
echo ' <span class="badge text-bg-danger"><i class="bi bi-eye-slash-fill"></i> ', UOJLocale::get('hidden'), '</span> ';
}
if (isset($_COOKIE['show_tags_mode'])) {
foreach (queryProblemTags($problem['id']) as $tag) {
echo ' <a class="uoj-problem-tag my-1">';
echo '<span class="badge bg-secondary">';
echo HTML::escape($tag), '</span>';
echo '</a> ';
}
}
echo '</td>';
if (isset($_COOKIE['show_submit_mode'])) {
$perc = $problem['submit_num'] > 0 ? round(100 * $problem['ac_num'] / $problem['submit_num']) : 0;
echo <<<EOD
<td><a class="text-decoration-none" href="/submissions?problem_id={$problem['id']}&min_score=100&max_score=100">&times;{$problem['ac_num']}</a></td>
<td><a class="text-decoration-none" href="/submissions?problem_id={$problem['id']}">&times;{$problem['submit_num']}</a></td>
<td>
<div class="progress bot-buffer-no">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="$perc" aria-valuemin="0" aria-valuemax="100" style="width: $perc%; min-width: 20px;">{$perc}%</div>
</div>
</td>
EOD;
}
if (isset($_COOKIE['show_difficulty'])) {
$extra_config = getProblemExtraConfig($problem);
if ($extra_config['difficulty'] == 0) {
echo "<td></td>";
} else {
echo "<td>{$extra_config['difficulty']}</td>";
}
}
echo '<td class="text-start">', getClickZanBlock('P', $problem['id'], $problem['zan'], null, false), '</td>';
echo '</tr>';
} }
} }
$html .= '</td>';
$cond = array();
$search_tag = null;
$cur_tab = isset($_GET['tab']) ? $_GET['tab'] : 'all';
if ($cur_tab == 'template') {
$search_tag = "模板题";
}
if (isset($_GET['tag'])) {
$search_tag = $_GET['tag'];
}
if ($search_tag) {
$cond[] = "'".DB::escape($search_tag)."' in (select tag from problems_tags where problems_tags.problem_id = problems.id)";
}
if (isset($_GET["search"])) {
$cond[]="(title like '%".DB::escape($_GET["search"])."%' or id like '%".DB::escape($_GET["search"])."%')";
}
if (isset($_GET['is_hidden'])) {
$cond[] = 'is_hidden = 1';
}
if (Auth::check() && isset($_GET['my'])) {
$cond[] = "uploader = '{$myUser['username']}'";
}
if ($cond) {
$cond = join($cond, ' and ');
} else {
$cond = '1';
}
$header = '<tr>';
$header .= '<th class="text-center" style="width:5em;">ID</th>';
$header .= '<th>'.UOJLocale::get('problems::problem').'</th>';
if (isset($_COOKIE['show_submit_mode'])) { if (isset($_COOKIE['show_submit_mode'])) {
$header .= '<th class="text-center" style="width:4em;">'.UOJLocale::get('problems::ac').'</th>'; $perc = $info['submit_num'] > 0 ? round(100 * $info['ac_num'] / $info['submit_num']) : 0;
$header .= '<th class="text-center" style="width:4em;">'.UOJLocale::get('problems::submit').'</th>'; $html .= '<td><a href="/submissions?problem_id=' . $info['id'] . '&min_score=100&max_score=100">&times;' . $info['ac_num'] . '</a></td>';
$header .= '<th class="text-center" style="width:125px;">'.UOJLocale::get('problems::ac ratio').'</th>'; $html .= '<td><a href="/submissions?problem_id=' . $info['id'] . '">&times;' . $info['submit_num'] . '</a></td>';
$html .= '<td>';
$html .= '<div class="progress bot-buffer-no">';
$html .= '<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="' . $perc . '" aria-valuemin="0" aria-valuemax="100" style="width: ' . $perc . '%; min-width: 20px;">';
$html .= $perc . '%';
$html .= '</div>';
$html .= '</div>';
$html .= '</td>';
} }
if (isset($_COOKIE['show_difficulty'])) { $html .= '<td class="text-center">' . ClickZans::getCntBlock($problem->info['zan']) . '</td>';
$header .= '<th class="text-center" style="width:3em;">'.UOJLocale::get('problems::difficulty').'</th>'; $html .= '</tr>';
return $html;
}
$cond = [];
$search_tag = UOJRequest::get('tag', 'is_string', null);
$search_content = UOJRequest::get('search', 'is_string', '');
$search_is_effective = false;
$cur_tab = UOJRequest::get('tab', 'is_string', 'all');
if ($cur_tab == 'template') {
$search_tag = "模板题";
}
if (is_string($search_tag)) {
$cond[] = [
DB::rawvalue($search_tag), "in", DB::rawbracket([
"select tag from problems_tags",
"where", ["problems_tags.problem_id" => DB::raw("problems.id")]
])
];
}
if ($search_content !== '') {
foreach (explode(' ', $search_content) as $key) {
if (strlen($key) > 0) {
$cond[] = DB::lor([
[DB::instr(DB::raw('title'), $key), '>', 0],
DB::exists([
"select tag from problems_tags",
"where", [
[DB::instr(DB::raw('tag'), $key), '>', 0],
"problems_tags.problem_id" => DB::raw("problems.id")
]
]),
"id" => $key,
]);
$search_is_effective = true;
}
} }
$header .= '<th class="text-center" style="width:100px;">'.UOJLocale::get('appraisal').'</th>'; }
$header .= '</tr>';
$tabs_info = array(
'all' => array(
'name' => UOJLocale::get('problems::all problems'),
'url' => "/problems"
),
'template' => array(
'name' => UOJLocale::get('problems::template problems'),
'url' => "/problems/template"
)
);
$pag_config = array('page_len' => 40); if (isset($_GET['is_hidden'])) {
$pag_config['col_names'] = array('best_ac_submissions.submission_id as submission_id', 'problems.id as id', 'problems.is_hidden as is_hidden', 'problems.title as title', 'problems.submit_num as submit_num', 'problems.ac_num as ac_num', 'problems.zan as zan', 'problems.extra_config as extra_config', 'problems.uploader as uploader', 'problems.extra_config as extra_config'); $cond[] = [
$pag_config['table_name'] = "problems left join best_ac_submissions on best_ac_submissions.submitter = '{$myUser['username']}' and problems.id = best_ac_submissions.problem_id"; 'is_hidden' => '1',
$pag_config['cond'] = $cond; ];
$pag_config['tail'] = "order by id asc"; }
$pag = new Paginator($pag_config);
$div_classes = ['card', 'my-3', 'table-responsive']; if (Auth::check() && isset($_GET['my'])) {
$table_classes = ['table', 'uoj-table', 'mb-0']; $cond[] = [
?> 'uploader' => Auth::id(),
];
}
if (!$cond) {
$cond = '1';
}
$header = '<tr>';
$header .= '<th class="text-center" style="width:5em;">ID</th>';
$header .= '<th>' . UOJLocale::get('problems::problem') . '</th>';
if (isset($_COOKIE['show_submit_mode'])) {
$header .= '<th class="text-center" style="width:4em;">' . UOJLocale::get('problems::ac') . '</th>';
$header .= '<th class="text-center" style="width:4em;">' . UOJLocale::get('problems::submit') . '</th>';
$header .= '<th class="text-center" style="width:125px;">' . UOJLocale::get('problems::ac ratio') . '</th>';
}
if (isset($_COOKIE['show_difficulty'])) {
$header .= '<th class="text-center" style="width:3em;">' . UOJLocale::get('problems::difficulty') . '</th>';
}
$header .= '<th class="text-center" style="width:50px;">' . UOJLocale::get('appraisal') . '</th>';
$header .= '</tr>';
$tabs_info = array(
'all' => array(
'name' => UOJLocale::get('problems::all problems'),
'url' => "/problems"
),
'template' => array(
'name' => UOJLocale::get('problems::template problems'),
'url' => "/problems/template"
)
);
$pag = new Paginator([
'col_names' => ['*'],
'table_name' => [
"problems left join best_ac_submissions",
"on", [
"best_ac_submissions.submitter" => Auth::id(),
"problems.id" => DB::raw("best_ac_submissions.problem_id")
],
],
'cond' => $cond,
'tail' => "order by id asc",
'page_len' => 40,
'post_filter' => function ($problem) {
return (new UOJProblem($problem))->userCanView(Auth::user());
}
]);
// if ($search_is_effective) {
// $search_summary = [
// 'count_in_cur_page' => $pag->countInCurPage(),
// 'first_a_few' => []
// ];
// foreach ($pag->get(5) as $info) {
// $problem = new UOJProblem($info);
// $search_summary['first_a_few'][] = [
// 'type' => 'problem',
// 'id' => $problem->info['id'],
// 'title' => $problem->getTitle()
// ];
// }
// DB::insert([
// "insert into search_requests",
// "(created_at, remote_addr, type, cache_id, q, content, result)",
// "values", DB::tuple([DB::now(), UOJContext::remoteAddr(), 'search', 0, $search_content, UOJContext::requestURI(), json_encode($search_summary)])
// ]);
// }
?>
<?php echoUOJPageHeader(UOJLocale::get('problems')) ?> <?php echoUOJPageHeader(UOJLocale::get('problems')) ?>
<div class="row"> <div class="row">
<!-- left col --> <!-- left col -->
<div class="col-lg-9"> <div class="col-lg-9">
<!-- title --> <!-- title -->
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<h1 class="h2"> <h1>
<?= UOJLocale::get('problems') ?> <?= UOJLocale::get('problems') ?>
</h1> </h1>
<?php if (isSuperUser($myUser) || isProblemManager($myUser) || isProblemUploader($myUser)): ?> <?php if (isSuperUser($myUser) || isProblemManager($myUser) || isProblemUploader($myUser)) : ?>
<div class="text-end"> <div class="text-end">
<?php $new_problem_form->printHTML(); ?> <?php $new_problem_form->printHTML(); ?>
</div> </div>
<?php endif ?> <?php endif ?>
</div> </div>
<!-- end title --> <!-- end title -->
<div class="row"> <div class="row">
<div class="col-sm-4 col-12"> <div class="col-sm-4 col-12">
<?= HTML::tablist($tabs_info, $cur_tab, 'nav-pills') ?> <?= HTML::tablist($tabs_info, $cur_tab, 'nav-pills') ?>
</div> </div>
<div class="text-end p-2 col-12 col-sm-8"> <div class="text-end p-2 col-12 col-sm-8">
<div class="form-check d-inline-block me-2"> <div class="form-check d-inline-block me-2">
<input <input type="checkbox" id="input-show_tags_mode" class="form-check-input" <?= isset($_COOKIE['show_tags_mode']) ? 'checked="checked" ' : '' ?> />
type="checkbox" id="input-show_tags_mode" <label class="form-check-label" for="input-show_tags_mode">
class="form-check-input" <?= UOJLocale::get('problems::show tags') ?>
<?= isset($_COOKIE['show_tags_mode']) ? 'checked="checked" ': ''?> </label>
/> </div>
<label class="form-check-label" for="input-show_tags_mode">
<?= UOJLocale::get('problems::show tags') ?> <div class="form-check d-inline-block">
</label> <input type="checkbox" id="input-show_submit_mode" class="form-check-input" <?= isset($_COOKIE['show_submit_mode']) ? 'checked="checked" ' : '' ?> />
<label class="form-check-label" for="input-show_submit_mode">
<?= UOJLocale::get('problems::show statistics') ?>
</label>
</div>
<div class="form-check d-inline-block">
<input type="checkbox" id="input-show_difficulty" class="form-check-input" <?= isset($_COOKIE['show_difficulty']) ? 'checked="checked" ' : '' ?> />
<label class="form-check-label" for="input-show_difficulty">
<?= UOJLocale::get('problems::show difficulty') ?>
</label>
</div>
</div>
</div> </div>
<div class="form-check d-inline-block"> <?= $pag->pagination() ?>
<input
type="checkbox" id="input-show_submit_mode" <script type="text/javascript">
class="form-check-input" $('#input-show_tags_mode').click(function() {
<?= isset($_COOKIE['show_submit_mode']) ? 'checked="checked" ': ''?> if (this.checked) {
/> $.cookie('show_tags_mode', '', {
<label class="form-check-label" for="input-show_submit_mode"> path: '/problems'
<?= UOJLocale::get('problems::show statistics') ?> });
</label> } else {
$.removeCookie('show_tags_mode', {
path: '/problems'
});
}
location.reload();
});
$('#input-show_submit_mode').click(function() {
if (this.checked) {
$.cookie('show_submit_mode', '', {
path: '/problems'
});
} else {
$.removeCookie('show_submit_mode', {
path: '/problems'
});
}
location.reload();
});
$('#input-show_difficulty').click(function() {
if (this.checked) {
$.cookie('show_difficulty', '', {
path: '/'
});
} else {
$.removeCookie('show_difficulty', {
path: '/'
});
}
location.reload();
});
</script>
<div class="card my-3">
<?=
HTML::responsive_table($header, $pag->get(), [
'table_attr' => [
'class' => ['table', 'uoj-table', 'mb-0'],
],
'tr' => function ($row, $idx) {
return getProblemTR($row);
}
]);
?>
</div> </div>
<div class="form-check d-inline-block"> <?= $pag->pagination() ?>
<input
type="checkbox" id="input-show_difficulty"
class="form-check-input"
<?= isset($_COOKIE['show_difficulty']) ? 'checked="checked" ': ''?>
/>
<label class="form-check-label" for="input-show_difficulty">
<?= UOJLocale::get('problems::show difficulty') ?>
</label>
</div>
</div> </div>
</div> <!-- end left col -->
<div class="text-center"> <!-- right col -->
<?= $pag->pagination() ?> <aside class="col-lg-3 mt-3 mt-lg-0">
</div>
<script type="text/javascript"> <!-- search bar -->
$('#input-show_tags_mode').click(function() { <form method="get" class="mb-3" id="form-problem_search">
if (this.checked) { <div class="input-group mb-3">
$.cookie('show_tags_mode', '', {path: '/problems'}); <input id="search-input" name="search" type="text" class="form-control" placeholder="搜索">
} else { <button class="btn btn-outline-secondary" type="submit">
$.removeCookie('show_tags_mode', {path: '/problems'}); <i class="bi bi-search"></i>
} </button>
location.reload(); </div>
}); <?php if (Auth::check()) : ?>
$('#input-show_submit_mode').click(function() { <div class="form-check d-inline-block">
if (this.checked) { <input type="checkbox" name="my" <?= isset($_GET['my']) ? 'checked="checked"' : '' ?> class="form-check-input" id="input-my">
$.cookie('show_submit_mode', '', {path: '/problems'}); <label class="form-check-label" for="input-my">
} else { 我的题目
$.removeCookie('show_submit_mode', {path: '/problems'}); </label>
} </div>
location.reload(); <?php endif ?>
}); <?php if (isProblemManager(Auth::user())) : ?>
$('#input-show_difficulty').click(function() { <div class="form-check d-inline-block ms-2">
if (this.checked) { <input type="checkbox" name="is_hidden" <?= isset($_GET['is_hidden']) ? 'checked="checked"' : '' ?> class="form-check-input" id="input-is_hidden">
$.cookie('show_difficulty', '', {path: '/'}); <label class="form-check-label" for="input-is_hidden">
} else { 隐藏题目
$.removeCookie('show_difficulty', {path: '/'}); </label>
} </div>
location.reload(); <?php endif ?>
}); </form>
</script> <script>
<div class="<?= join($div_classes, ' ') ?>"> $('#search-input').val(new URLSearchParams(location.search).get('search'));
<table class="<?= join($table_classes, ' ') ?>"> $('#input-my, #input-is_hidden').click(function() {
<thead><?= $header ?></thead> $('#form-problem_search').submit();
<tbody> });
<?php </script>
foreach ($pag->get() as $idx => $row) {
echoProblem($row);
echo "\n";
}
if ($pag->isEmpty()) {
echo '<tr><td class="text-center" colspan="233">'.UOJLocale::get('none').'</td></tr>';
}
?>
</tbody>
</table>
</div>
<div class="text-center"> <!-- sidebar -->
<?= $pag->pagination() ?> <?php uojIncludeView('sidebar') ?>
</div> </aside>
</div>
<!-- end left col -->
<!-- right col -->
<aside class="col-lg-3 mt-3 mt-lg-0">
<!-- search bar -->
<form method="get" class="mb-3" id="form-problem_search">
<div class="input-group mb-3">
<input id="search-input" name="search" type="text" class="form-control" placeholder="搜索">
<button class="btn btn-outline-secondary" type="submit">
<i class="bi bi-search"></i>
</button>
</div>
<?php if (Auth::check()): ?>
<div class="form-check d-inline-block">
<input
type="checkbox" name="my"
<?= isset($_GET['my']) ? 'checked="checked"' : '' ?>
class="form-check-input" id="input-my">
<label class="form-check-label" for="input-my">
我的题目
</label>
</div>
<?php endif ?>
<?php if (isProblemManager(Auth::user())): ?>
<div class="form-check d-inline-block ms-2">
<input
type="checkbox" name="is_hidden"
<?= isset($_GET['is_hidden']) ? 'checked="checked"' : '' ?>
class="form-check-input" id="input-is_hidden">
<label class="form-check-label" for="input-is_hidden">
隐藏题目
</label>
</div>
<?php endif ?>
</form>
<script>
$('#search-input').val(new URLSearchParams(location.search).get('search'));
$('#input-my, #input-is_hidden').click(function() {
$('#form-problem_search').submit();
});
</script>
<!-- sidebar -->
<?php uojIncludeView('sidebar', []) ?>
</aside>
</div> </div>

View File

@ -1,305 +1,270 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requireLib('hljs'); requireLib('hljs');
requireLib('mathjax'); requireLib('mathjax');
requirePHPLib('form'); requirePHPLib('form');
requirePHPLib('judger'); requirePHPLib('judger');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin(); UOJProblem::init(UOJRequest::get('id')) || UOJResponse::page404();
UOJProblem::cur()->userCanView(Auth::user()) || UOJResponse::page403();
if (!UOJProblem::cur()->userCanManage(Auth::user())) {
UOJProblem::cur()->userPermissionCodeCheck(Auth::user(), UOJProblem::cur()->getExtraConfig('view_solution_type')) || UOJResponse::page403();
foreach (UOJProblem::cur()->findInContests() as $cp) {
if ($cp->contest->progress() == CONTEST_IN_PROGRESS && $cp->contest->userHasRegistered(Auth::user())) {
UOJResponse::page403();
}
}
}
if (UOJRequest::post('submit-remove_solution') === 'remove_solution') {
crsf_defend();
$blog = UOJBlog::query(UOJRequest::post('blog_id'));
if (!$blog || !$blog->userCanView(Auth::user())) {
dieWithAlert('博客不存在。');
} }
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { if (!UOJProblem::cur()->userCanManage(Auth::user()) && $blog->info['poster'] != Auth::id()) {
become403Page(); dieWithAlert('您没有权限移除该题解。');
} }
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) { if ($blog->info['poster'] != Auth::id()) {
become404Page(); sendSystemMsg(
$blog->info['poster'],
'题解移除通知',
"<p>" . UOJUser::getLink($blog->info['poster']) . " 您好:</p>" .
"<p>您为问题 " . UOJProblem::cur()->getLink(['with' => 'id']) . " 提交的题解 " . $blog->getLink() . " 已被" . (isSuperUser(Auth::user()) ? "管理员" : " " . UOJUser::getLink(Auth::user()) . " ") . "移除。</p>"
);
} }
if (!isProblemVisibleToUser($problem, $myUser)) { DB::delete([
become404Page(); "delete from problems_solutions",
} "where", [
"problem_id" => UOJProblem::info('id'),
"blog_id" => $blog->info['id'],
],
]);
$problem_extra_config = getProblemExtraConfig($problem); dieWithAlert('移除成功!');
$solution_viewable = hasViewSolutionPermission($problem_extra_config['view_solution_type'], $myUser, $problem); }
$solution_submittable = hasViewSolutionPermission($problem_extra_config['submit_solution_type'], $myUser, $problem);
if (!$solution_viewable) { if (UOJProblem::cur()->userCanManage(Auth::user()) || UOJProblem::cur()->userPermissionCodeCheck(Auth::user(), UOJProblem::cur()->getExtraConfig('submit_solution_type'))) {
become403Page(); $add_new_solution_form = new UOJBs4Form('add_new_solution');
} $add_new_solution_form->addVInput(
'blog_id_2',
if (!hasProblemPermission($myUser, $problem) && isRegisteredRunningContestProblem($myUser, $problem)) { 'text',
become403Page(); '博客 ID',
} '',
function ($blog_id) {
function removeSolutionForm($blog_id) { $blog = UOJBlog::query($blog_id);
$res_form = new UOJForm("remove_solution_{$blog_id}");
$res_form->addHidden("blog_id", $blog_id, function($blog_id) {
global $myUser, $problem;
if (!validateUInt($blog_id)) {
return '博客 ID 不是有效的数字';
}
$blog = queryBlog($blog_id);
if (!$blog) { if (!$blog) {
return '博客不存在'; return '博客不存在';
} }
if (!hasProblemPermission($myUser, $problem)) { if (!$blog->userCanManage(Auth::user())) {
if ($blog['poster'] != $myUser['username']) { if ($blog->info['poster'] != Auth::id()) {
return '您只能删除自己的题解'; if ($blog->info['is_hidden']) {
return '博客不存在';
}
return '只能提交本人撰写的博客';
} }
} }
if (!UOJProblem::cur()->userCanManage(Auth::user())) {
if ($blog->info['is_hidden']) {
return '只能提交公开的博客';
}
}
if (querySolution(UOJProblem::info('id'), $blog_id)) {
return '该题解已提交';
}
return ''; return '';
}, null); },
$res_form->handle = function() { null
global $myUser, $problem; );
$add_new_solution_form->submit_button_config['text'] = '发布';
$add_new_solution_form->submit_button_config['align'] = 'center';
$add_new_solution_form->submit_button_config['class_str'] = 'btn btn-secondary';
$add_new_solution_form->handle = function () {
DB::insert([
"insert into problems_solutions",
DB::bracketed_fields(["problem_id", "blog_id"]),
"values", DB::tuple([UOJProblem::info('id'), $_POST['blog_id_2']]),
]);
};
$add_new_solution_form->runAtServer();
}
$blog_id = $_POST["blog_id"]; $pag_config = [
DB::query("delete from problems_solutions where problem_id = {$problem['id']} and blog_id = {$blog_id}"); 'page_len' => 5,
$blog = queryBlog($blog_id); 'col_names' => ['blog_id', 'zan'],
'table_name' => 'problems_solutions inner join blogs on blogs.id = problems_solutions.blog_id',
'cond' => ["problem_id" => UOJProblem::info('id')],
'post_filter' => function ($row) {
$blog = UOJBlog::query($row['blog_id']);
if ($blog['poster'] != $myUser['username']) { // 根据实际使用需要,题目管理员可以通过题解页面看到其他用户提交的题解,并且即使该题解对应的博客是隐藏状态也会照常显示
$blog_link = getBlogLink($blog['id']); // 如需仅允许超级管理员查看,请将下一行改为 return $blog->userCanView(Auth::user());
$poster_user_link = getUserLink($blog['poster']); return $blog->userCanView(Auth::user()) || UOJProblem::cur()->userCanManage(Auth::user());
$admin_user_link = isSuperUser($myUser) ? '管理员' : getUserLink($myUser['username']); },
$content = <<<EOD ];
<p>{$poster_user_link} 您好:</p>
<p>您为问题 <a href="/problem/{$problem['id']}">#{$problem['id']} ({$problem['title']})</a> 提交的题解 {$blog_link} 已经被 {$admin_user_link} 移除。 </p>
EOD;
sendSystemMsg($blog['poster'], '题解移除通知', $content);
}
};
$res_form->submit_button_config['margin_class'] = 'mt-0';
$res_form->submit_button_config['class_str'] = 'btn btn-link text-decoration-none text-danger p-0';
$res_form->submit_button_config['text'] = '移除';
$res_form->submit_button_config['confirm_text'] = '你真的要移除这篇题解吗?';
$res_form->submit_button_config['align'] = 'inline';
return $res_form; $pag_config['tail'] = "order by zan desc, post_time desc, id asc";
} $pag = new Paginator($pag_config);
?>
if ($solution_submittable) {
$add_new_solution_form = new UOJForm('add_new_solution');
$add_new_solution_form->addVInput('blog_id_2', 'text', '博客 ID', '',
function ($x) {
global $myUser, $problem, $solution_submittable;
if (!validateUInt($x)) {
return 'ID 不合法';
}
$blog = queryBlog($x);
if (!$blog) {
return '博客不存在';
}
if (!isSuperUser($myUser)) {
if ($blog['poster'] != $myUser['username']) {
if ($blog['is_hidden']) {
return '博客不存在';
}
return '只能提交本人撰写的博客';
}
}
if (!hasProblemPermission($myUser, $problem)) {
if ($blog['is_hidden']) {
return '只能提交公开的博客';
}
}
if (querySolution($problem['id'], $x)) {
return '该题解已提交';
}
if (!$solution_submittable) {
return '您无权提交题解';
}
return '';
},
null
);
$add_new_solution_form->submit_button_config['text'] = '发布';
$add_new_solution_form->submit_button_config['align'] = 'center';
$add_new_solution_form->handle = function() {
global $problem, $myUser;
$blog_id_2 = $_POST['blog_id_2'];
$problem_id = $problem['id'];
DB::insert("insert into problems_solutions (problem_id, blog_id) values ({$problem_id}, {$blog_id_2})");
};
$add_new_solution_form->runAtServer();
}
$pag_config = array('page_len' => 5);
$pag_config['col_names'] = array('blog_id', 'content', 'poster', 'post_time', 'zan', 'is_hidden');
$pag_config['table_name'] = "problems_solutions inner join blogs on problems_solutions.blog_id = blogs.id";
$pag_config['cond'] = "problem_id = {$problem['id']}";
// 根据实际使用需要,题目管理员可以通过题解页面看到其他用户提交的题解,并且即使该题解对应的博客是隐藏状态也会照常显示
// 如需仅允许超级管理员查看,请将下一行中 if 语句的条件改为 (!isSuperUser($myUser))
if (!hasProblemPermission($myUser, $problem)) {
$pag_config['cond'] .= " and (is_hidden = 0 or poster = '{$myUser['username']}')";
}
$pag_config['tail'] = "order by zan desc, post_time desc, id asc";
$pag = new Paginator($pag_config);
$rows = [];
foreach ($pag->get() as $idx => $row) {
$rows[$idx] = $row;
if ($row['poster'] == $myUser['username'] || hasProblemPermission($myUser, $problem)) {
$removeForm = removeSolutionForm($row['blog_id']);
$removeForm->runAtServer();
$rows[$idx]['removeForm'] = $removeForm;
}
}
?>
<?php echoUOJPageHeader(UOJLocale::get('problems::solutions') . ' - ' . HTML::stripTags($problem['title'])) ?> <?php echoUOJPageHeader(UOJLocale::get('problems::solutions') . ' - ' . HTML::stripTags($problem['title'])) ?>
<div class="row"> <div class="row">
<!-- Left col -->
<!-- Left col --> <div class="col-lg-9">
<div class="col-lg-9"> <div class="card card-default mb-2">
<div class="card-body">
<div class="card card-default mb-2"> <h1 class="card-title text-center">
<div class="card-body"> <?= UOJProblem::cur()->getTitle(['with' => 'id']) ?>
<h1 class="h2 card-title text-center"> </h1>
#<?= $problem['id']?>. <?= $problem['title'] ?>
</h1>
</div>
<ul class="list-group list-group-flush">
<?php foreach ($rows as $row): ?>
<li class="list-group-item">
<?php $poster = queryUser($row['poster']); ?>
<div class="mb-3">
<span class="me-2 d-inline-block">
<a class="text-decoration-none" href="<?= HTML::url('/user/'.$poster['username']) ?>">
<img src="<?= HTML::avatar_addr($poster, 64) ?>" width="32" height="32" class="rounded" />
</a>
<?= getUserLink($poster['username']) ?>
</span>
<span class="text-muted small d-inline-block">
<?= UOJLocale::get('post time') ?>:
<time class="text-muted"><?= $row['post_time'] ?></time>
</span>
<?php if ($row['is_hidden']): ?>
<span class="badge text-bg-danger ms-2">
<i class="bi bi-eye-slash-fill"></i>
<?= UOJLocale::get('hidden') ?>
</span>
<?php endif ?>
</div>
<div class="markdown-body">
<?= $row['content'] ?>
</div>
<ul class="mt-3 text-end list-inline">
<?php if (isset($row['removeForm'])): ?>
<li class="list-inline-item">
<?php $row['removeForm']->printHTML(); ?>
</li>
<?php endif ?>
<?php if (Auth::check() && (isSuperUser(Auth::user()) || Auth::id() == $row['poster'])): ?>
<li class="list-inline-item">
<a class="text-decoration-none" href="<?= HTML::blog_url($row['poster'], '/post/'.$row['blog_id'].'/write') ?>">
修改
</a>
</li>
<?php endif ?>
<li class="list-inline-item">
<a class="text-decoration-none" href="/blogs/<?= $row['blog_id'] ?>"> Ta 的博客上查看</a>
<li class="list-inline-item">
<?= getClickZanBlock('B', $row['blog_id'], $row['zan']) ?>
</li>
</ul>
</li>
<?php endforeach ?>
<?php if ($pag->isEmpty()): ?>
<div class="text-center py-2">
暂无题解
</div> </div>
<?php endif ?>
</ul>
</div>
<!-- Pagination --> <ul class="list-group list-group-flush">
<?= $pag->pagination() ?> <?php foreach ($pag->get() as $row) : ?>
<?php
$blog = UOJBlog::query($row['blog_id']);
$poster = UOJUser::query($blog->info['poster']);
?>
<li class="list-group-item">
<div class="mb-3">
<span class="me-2 d-inline-block">
<a class="text-decoration-none" href="<?= HTML::url('/user/' . $poster['username']) ?>">
<img src="<?= HTML::avatar_addr($poster, 64) ?>" width="32" height="32" class="rounded" />
</a>
<?= UOJUser::getLink($poster) ?>
</span>
<span class="text-muted small d-inline-block">
<?= UOJLocale::get('post time') ?>:
<time class="text-muted"><?= $blog->info['post_time'] ?></time>
</span>
<?php if ($blog->info['is_hidden']) : ?>
<span class="badge text-bg-danger ms-2">
<i class="bi bi-eye-slash-fill"></i>
<?= UOJLocale::get('hidden') ?>
</span>
<?php endif ?>
</div>
<!-- End left col --> <div class="markdown-body">
</div> <?= $blog->queryContent()['content'] ?>
</div>
<!-- Right col --> <ul class="mt-3 text-end list-inline">
<aside class="col-lg-3 mt-3 mt-lg-0"> <?php if (UOJProblem::cur()->userCanManage(Auth::user()) || $poster['username'] == Auth::id()) : ?>
<li class="list-inline-item">
<form class="d-inline-block" method="POST" onsubmit="return confirm('你真的要移除这篇题解吗?移除题解不会删除对应博客。')">
<input type="hidden" name="_token" value="<?= crsf_token() ?>">
<input type="hidden" name="blog_id" value="<?= $blog->info['id'] ?>">
<button class="btn btn-link text-decoration-none text-danger p-0 mt-0" type="submit" name="submit-remove_solution" value="remove_solution">
移除
</button>
</form>
</li>
<?php endif ?>
<?php if ($blog->userCanManage(Auth::user())) : ?>
<li class="list-inline-item">
<a class="d-inline-block align-middle" href="<?= $blog->getUriForWrite() ?>">
修改
</a>
</li>
<?php endif ?>
<li class="list-inline-item">
<a class="d-inline-block align-middle" href="<?= $blog->getBlogUri() ?>">
Ta 的博客上查看
</a>
</li>
<li class="list-inline-item">
<?= ClickZans::getBlock('B', $blog->info['id'], $blog->info['zan']) ?>
</li>
</ul>
</li>
<?php endforeach ?>
<?php if ($pag->isEmpty()) : ?>
<div class="text-center text-muted py-4">
暂无题解
</div>
<?php endif ?>
</ul>
</div>
<!-- Pagination -->
<?= $pag->pagination() ?>
<div class="card card-default mb-2">
<ul class="nav nav-pills nav-fill flex-column" role="tablist">
<li class="nav-item text-start">
<a href="/problem/<?= $problem['id'] ?>" class="nav-link" role="tab">
<i class="bi bi-journal-text"></i>
<?= UOJLocale::get('problems::statement') ?>
</a>
</li>
<li class="nav-item text-start">
<a href="#" class="nav-link active" role="tab">
<i class="bi bi-journal-bookmark"></i>
<?= UOJLocale::get('problems::solutions') ?>
</a>
</li>
<li class="nav-item text-start">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/statistics">
<i class="bi bi-graph-up"></i>
<?= UOJLocale::get('problems::statistics') ?>
</a>
</li>
<?php if (hasProblemPermission($myUser, $problem)): ?>
<li class="nav-item text-start">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab">
<i class="bi bi-sliders"></i>
<?= UOJLocale::get('problems::manage') ?>
</a>
</li>
<?php endif ?>
</ul>
<div class="card-footer bg-transparent">
评价:<?= getClickZanBlock('P', $problem['id'], $problem['zan']) ?>
</div> </div>
</div> <!-- End left col -->
<div class="card card-default mb-2"> <!-- Right col -->
<div class="card-header bg-transparent fw-bold"> <aside class="col-lg-3 mt-3 mt-lg-0">
增加题解
</div>
<div class="card-body">
<?php if (isset($add_new_solution_form)): ?>
<?php $add_new_solution_form->printHTML(); ?>
<?php else: ?>
您当前无法为本题新增题解。
<?php endif ?>
</div>
<div class="card-footer bg-transparent">
<a target="_blank" class="text-decoration-none" href="<?= HTML::blog_url(Auth::id(), '/post/new/write?title=' . urlencode('【题解】#' . $problem['id'] . '. ' . $problem['title']) . '&is_hidden=0&add_solution_for=' . $problem['id']) ?>">
快速新建文章
</a>
<div class="small text-muted mt-1">发布文章后,请返回本页输入博客 ID</div>
</div>
</div>
<?php uojIncludeView('sidebar', array()); ?> <div class="card card-default mb-2">
<ul class="nav nav-pills nav-fill flex-column" role="tablist">
<li class="nav-item text-start">
<a href="/problem/<?= UOJProblem::info('id') ?>" class="nav-link" role="tab">
<i class="bi bi-journal-text"></i>
<?= UOJLocale::get('problems::statement') ?>
</a>
</li>
<li class="nav-item text-start">
<a href="#" class="nav-link active" role="tab">
<i class="bi bi-journal-bookmark"></i>
<?= UOJLocale::get('problems::solutions') ?>
</a>
</li>
<li class="nav-item text-start">
<a class="nav-link" href="/problem/<?= UOJProblem::info('id') ?>/statistics">
<i class="bi bi-graph-up"></i>
<?= UOJLocale::get('problems::statistics') ?>
</a>
</li>
<?php if (UOJProblem::cur()->userCanManage(Auth::user())) : ?>
<li class="nav-item text-start">
<a class="nav-link" href="/problem/<?= UOJProblem::info('id') ?>/manage/statement" role="tab">
<i class="bi bi-sliders"></i>
<?= UOJLocale::get('problems::manage') ?>
</a>
</li>
<?php endif ?>
</ul>
<div class="card-footer bg-transparent">
评价:<?= ClickZans::getBlock('P', UOJProblem::info('id'), UOJProblem::info('zan')) ?>
</div>
</div>
<!-- End right col --> <div class="card card-default mb-2">
</aside> <div class="card-header bg-transparent fw-bold">
增加题解
</div>
<div class="card-body">
<?php if (isset($add_new_solution_form)) : ?>
<?php $add_new_solution_form->printHTML(); ?>
<?php else : ?>
您当前无法为本题新增题解。
<?php endif ?>
</div>
<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>
</div>
</div>
<?php uojIncludeView('sidebar'); ?>
<!-- End right col -->
</aside>
</div> </div>

View File

@ -1,144 +1,154 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requirePHPLib('form'); requirePHPLib('form');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin();
}
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) { UOJProblem::init(UOJRequest::get('id')) || UOJResponse::page404();
become404Page(); UOJProblem::cur()->userCanManage(Auth::user()) || UOJResponse::page403();
$problem_content = UOJProblem::cur()->queryContent();
$problem_editor = new UOJBlogEditor();
$problem_editor->name = 'problem';
$problem_editor->blog_url = '/problem/' . UOJProblem::info('id');
$problem_editor->cur_data = [
'title' => UOJProblem::info('title'),
'content_md' => $problem_content['statement_md'],
'content' => $problem_content['statement'],
'tags' => UOJProblem::cur()->queryTags(),
'is_hidden' => UOJProblem::info('is_hidden')
];
$problem_editor->label_text = array_merge($problem_editor->label_text, [
'view blog' => '查看题目',
'blog visibility' => '题目可见性'
]);
$problem_editor->save = function ($data) {
DB::update([
"update problems",
"set", ["title" => $data['title']],
"where", ["id" => UOJProblem::info('id')]
]);
DB::update([
"update problems_contents",
"set", [
"statement" => $data['content'],
"statement_md" => $data['content_md']
], "where", ["id" => UOJProblem::info('id')]
]);
UOJProblem::cur()->updateTags($data['tags']);
if ($data['is_hidden'] != UOJProblem::info('is_hidden')) {
DB::update([
"update problems",
"set", ["is_hidden" => $data['is_hidden']],
"where", ["id" => UOJProblem::info('id')]
]);
DB::update([
"update submissions",
"set", ["is_hidden" => $data['is_hidden']],
"where", ["problem_id" => UOJProblem::info('id')]
]);
DB::update([
"update hacks",
"set", ["is_hidden" => $data['is_hidden']],
"where", ["problem_id" => UOJProblem::info('id')]
]);
} }
if (!hasProblemPermission($myUser, $problem)) { };
become403Page();
} $problem_editor->runAtServer();
?>
$problem_content = queryProblemContent($problem['id']);
$problem_tags = queryProblemTags($problem['id']); <?php echoUOJPageHeader('题面编辑 - ' . HTML::stripTags(UOJProblem::info('title'))) ?>
$problem_editor = new UOJBlogEditor();
$problem_editor->name = 'problem';
$problem_editor->blog_url = "/problem/{$problem['id']}";
$problem_editor->cur_data = array(
'title' => $problem['title'],
'content_md' => $problem_content['statement_md'],
'content' => $problem_content['statement'],
'tags' => $problem_tags,
'is_hidden' => $problem['is_hidden']
);
$problem_editor->label_text = array_merge($problem_editor->label_text, array(
'view blog' => '查看题目',
'blog visibility' => '题目可见性'
));
$problem_editor->save = function($data) {
global $problem, $problem_tags;
DB::update("update problems set title = '".DB::escape($data['title'])."' where id = {$problem['id']}");
DB::update("update problems_contents set statement = '".DB::escape($data['content'])."', statement_md = '".DB::escape($data['content_md'])."' where id = {$problem['id']}");
if ($data['tags'] !== $problem_tags) {
DB::delete("delete from problems_tags where problem_id = {$problem['id']}");
foreach ($data['tags'] as $tag) {
DB::insert("insert into problems_tags (problem_id, tag) values ({$problem['id']}, '".DB::escape($tag)."')");
}
}
if ($data['is_hidden'] != $problem['is_hidden'] ) {
DB::update("update problems set is_hidden = {$data['is_hidden']} where id = {$problem['id']}");
DB::update("update submissions set is_hidden = {$data['is_hidden']} where problem_id = {$problem['id']}");
DB::update("update hacks set is_hidden = {$data['is_hidden']} where problem_id = {$problem['id']}");
}
};
$problem_editor->runAtServer();
?>
<?php echoUOJPageHeader(HTML::stripTags($problem['title']) . ' - 编辑 - 题目管理') ?>
<div class="row"> <div class="row">
<!-- left col --> <!-- left col -->
<div class="col-lg-9"> <div class="col-lg-9">
<h1>
<?= UOJProblem::cur()->getTitle(['with' => 'id']) ?> 管理
</h1>
<h1 class="h2"> <ul class="nav nav-pills my-3" role="tablist">
#<?=$problem['id']?>. <?=$problem['title']?> 管理 <li class="nav-item">
</h1> <a class="nav-link active" href="/problem/<?= UOJProblem::info('id') ?>/manage/statement" role="tab">
题面
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/problem/<?= UOJProblem::info('id') ?>/manage/managers" role="tab">
管理者
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/problem/<?= UOJProblem::info('id') ?>/manage/data" role="tab">
数据
</a>
</li>
</ul>
<ul class="nav nav-pills my-3" role="tablist"> <div class="card card-default">
<li class="nav-item"> <div class="card-body">
<a class="nav-link active" href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab"> <?php $problem_editor->printHTML() ?>
题面 </div>
</a> </div>
</li>
<li class="nav-item">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/manage/managers" role="tab">
管理者
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/manage/data" role="tab">
数据
</a>
</li>
</ul>
<div class="card card-default"> <!-- 提示信息 -->
<div class="card-body"> <div class="card mt-3">
<?php $problem_editor->printHTML() ?> <div class="card-body">
</div> <h2 class="h3 card-title">提示</h2>
</div> <ol>
<li>请勿引用不稳定的外部资源(如来自个人服务器的图片或文档等),以便备份及后期维护;</li>
<!-- 提示信息 --> <li>请勿在题面中直接插入大段 HTML 代码,这可能会破坏页面的显示,可以考虑使用 <a class="text-decoration-none" href="/html2markdown" target="_blank">转换工具</a> 转换后再作修正;</li>
<div class="card mt-3"> <li>图片上传推荐使用 <a class="text-decoration-none" href="/image_hosting" target="_blank">S2OJ 图床</a>,以免后续产生外链图片大量失效的情况。</li>
<div class="card-body"> </ol>
<h2 class="h4 card-title">提示</h2> <p class="card-text">
<ol> 更多内容请查看 S2OJ 用户手册中的「<a class="text-decoration-none" href="https://s2oj.github.io/#/manage/problem?id=%e4%bc%a0%e9%a2%98%e6%8c%87%e5%bc%95">传题指引</a>」部分。
<li>请勿引用不稳定的外部资源(如来自个人服务器的图片或文档等),以便备份及后期维护;</li> </p>
<li>请勿在题面中直接插入大段 HTML 代码,这可能会破坏页面的显示,可以考虑使用 <a class="text-decoration-none" href="/html2markdown" target="_blank">转换工具</a> 转换后再作修正;</li> </div>
<li>图片上传推荐使用 <a class="text-decoration-none" href="/image_hosting" target="_blank">S2OJ 图床</a>,以免后续产生外链图片大量失效的情况。</li> </div>
</ol>
<p class="card-text">
更多内容请查看 S2OJ 用户手册中的「<a class="text-decoration-none" href="https://s2oj.github.io/#/manage/problem?id=%e4%bc%a0%e9%a2%98%e6%8c%87%e5%bc%95">传题指引</a>」部分。
</p>
</div>
</div>
</div>
<!-- right col -->
<aside class="col-lg-3 mt-3 mt-lg-0">
<div class="card card-default mb-2">
<ul class="nav nav-pills nav-fill flex-column" role="tablist">
<li class="nav-item text-start">
<a href="/problem/<?= $problem['id'] ?>" class="nav-link" role="tab">
<i class="bi bi-journal-text"></i>
<?= UOJLocale::get('problems::statement') ?>
</a>
</li>
<li class="nav-item text-start">
<a href="/problem/<?= $problem['id'] ?>/solutions" class="nav-link" role="tab">
<i class="bi bi-journal-bookmark"></i>
<?= UOJLocale::get('problems::solutions') ?>
</a>
</li>
<li class="nav-item text-start">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/statistics">
<i class="bi bi-graph-up"></i>
<?= UOJLocale::get('problems::statistics') ?>
</a>
</li>
<li class="nav-item text-start">
<a class="nav-link active" href="#" role="tab">
<i class="bi bi-sliders"></i>
<?= UOJLocale::get('problems::manage') ?>
</a>
</li>
</ul>
<div class="card-footer bg-transparent">
评价:<?= getClickZanBlock('P', $problem['id'], $problem['zan']) ?>
</div> </div>
</div>
<?php uojIncludeView('sidebar', array()) ?> <!-- right col -->
</aside> <aside class="col-lg-3 mt-3 mt-lg-0">
<div class="card card-default mb-2">
<ul class="nav nav-pills nav-fill flex-column" role="tablist">
<li class="nav-item text-start">
<a href="/problem/<?= UOJProblem::info('id') ?>" class="nav-link" role="tab">
<i class="bi bi-journal-text"></i>
<?= UOJLocale::get('problems::statement') ?>
</a>
</li>
<li class="nav-item text-start">
<a href="/problem/<?= UOJProblem::info('id') ?>/solutions" class="nav-link" role="tab">
<i class="bi bi-journal-bookmark"></i>
<?= UOJLocale::get('problems::solutions') ?>
</a>
</li>
<li class="nav-item text-start">
<a class="nav-link" href="/problem/<?= UOJProblem::info('id') ?>/statistics">
<i class="bi bi-graph-up"></i>
<?= UOJLocale::get('problems::statistics') ?>
</a>
</li>
<li class="nav-item text-start">
<a class="nav-link active" href="#" role="tab">
<i class="bi bi-sliders"></i>
<?= UOJLocale::get('problems::manage') ?>
</a>
</li>
</ul>
<div class="card-footer bg-transparent">
评价:<?= ClickZans::getBlock('P', UOJProblem::info('id'), UOJProblem::info('zan')) ?>
</div>
</div>
<?php uojIncludeView('sidebar') ?>
</aside>
</div> </div>

View File

@ -1,351 +1,329 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requireLib('morris'); requireLib('morris');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin(); UOJProblem::init(UOJRequest::get('id')) || UOJResponse::page404();
}
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) { $problem = UOJProblem::cur()->info;
become404Page();
}
$contest = validateUInt($_GET['contest_id']) ? queryContest($_GET['contest_id']) : null;
if ($contest != null) {
genMoreContestInfo($contest);
if (!isContestProblemVisibleToUser($problem, $contest, $myUser)) {
become404Page();
}
$problem_rank = queryContestProblemRank($contest, $problem); if (UOJRequest::get('contest_id')) {
if ($problem_rank == null) { UOJContest::init(UOJRequest::get('contest_id')) || UOJResponse::page404();
become404Page(); UOJProblem::upgradeToContestProblem() || UOJResponse::page404();
} else { UOJContest::cur()->userCanSeeProblemStatistics(Auth::user()) || UOJResponse::page403();
$problem_letter = chr(ord('A') + $problem_rank - 1); }
} UOJProblem::cur()->userCanView(Auth::user(), ['ensure' => true]);
} else {
if (!isProblemVisibleToUser($problem, $myUser)) {
become404Page();
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { function scoreDistributionData() {
become403Page(); $data = array();
$res = DB::selectAll([
"select score, count(*) from submissions",
"where", [
"problem_id" => UOJProblem::info('id'),
["score", "is not", null],
UOJSubmission::sqlForUserCanView(Auth::user(), UOJProblem::cur())
], "group by score", "order by score"
], DB::NUM);
$has_score_0 = false;
$has_score_100 = false;
foreach ($res as $row) {
if ($row[0] == 0) {
$has_score_0 = true;
} else if ($row[0] == 100) {
$has_score_100 = true;
} }
$score = $row[0] * 100;
$data[] = ['score' => $score, 'count' => $row[1]];
} }
if (!$has_score_0) {
array_unshift($data, ['score' => 0, 'count' => 0]);
}
if (!$has_score_100) {
$data[] = ['score' => 10000, 'count' => 0];
}
return $data;
}
function scoreDistributionData() { $data = scoreDistributionData();
$data = array(); $pre_data = $data;
$result = DB::select("select score, count(*) from submissions where problem_id = {$_GET['id']} and score is not null group by score"); $suf_data = $data;
$is_res_empty = true; for ($i = 0; $i < count($data); $i++) {
$has_score_0 = false; $data[$i]['score'] /= 100;
$has_score_100 = false; }
while ($row = DB::fetch($result, MYSQLI_NUM)) { for ($i = 1; $i < count($data); $i++) {
if ($row[0] == 0) { $pre_data[$i]['count'] += $pre_data[$i - 1]['count'];
$has_score_0 = true; }
} elseif ($row[0] == 100) { for ($i = count($data) - 1; $i > 0; $i--) {
$has_score_100 = true; $suf_data[$i - 1]['count'] += $suf_data[$i]['count'];
} }
$score = $row[0] * 100;
$data[] = array('score' => $score, 'count' => $row[1]);
}
if (!$has_score_0) {
array_unshift($data, array('score' => 0, 'count' => 0));
}
if (!$has_score_100) {
$data[] = array('score' => 10000, 'count' => 0);
}
return $data;
}
$data = scoreDistributionData();
$pre_data = $data;
$suf_data = $data;
for ($i = 0; $i < count($data); $i++) {
$data[$i]['score'] /= 100;
}
for ($i = 1; $i < count($data); $i++) {
$pre_data[$i]['count'] += $pre_data[$i - 1]['count'];
}
for ($i = count($data) - 1; $i > 0; $i--) {
$suf_data[$i - 1]['count'] += $suf_data[$i]['count'];
}
$submissions_sort_by_choice = !isset($_COOKIE['submissions-sort-by-code-length']) ? 'time' : 'tot_size';
?>
<?php echoUOJPageHeader(HTML::stripTags($problem['title']) . ' - ' . UOJLocale::get('problems::statistics')) ?> $submissions_sort_by_choice = !isset($_COOKIE['submissions-sort-by-code-length']) ? 'time' : 'tot_size';
?>
<?php echoUOJPageHeader(HTML::stripTags(UOJProblem::info('title')) . ' - ' . UOJLocale::get('problems::statistics')) ?>
<div class="row"> <div class="row">
<!-- left col -->
<div class="col-lg-9">
<?php if (UOJContest::cur()) : ?>
<!-- 比赛导航 -->
<?php
$tabs_info = [
'dashboard' => [
'name' => UOJLocale::get('contests::contest dashboard'),
'url' => '/contest/' . UOJContest::info('id'),
],
'submissions' => [
'name' => UOJLocale::get('contests::contest submissions'),
'url' => '/contest/' . UOJContest::info('id') . '/submissions',
],
'standings' => [
'name' => UOJLocale::get('contests::contest standings'),
'url' => '/contest/' . UOJContest::info('id') . '/standings',
],
];
<!-- left col --> if (UOJContest::cur()->progress() > CONTEST_TESTING) {
<div class="col-lg-9"> $tabs_info['after_contest_standings'] = [
'name' => UOJLocale::get('contests::after contest standings'),
'url' => '/contest/' . UOJContest::info('id') . '/after_contest_standings',
];
$tabs_info['self_reviews'] = [
'name' => UOJLocale::get('contests::contest self reviews'),
'url' => '/contest/' . UOJContest::info('id') . '/self_reviews',
];
}
<?php if ($contest): ?> if (UOJContest::cur()->userCanManage(Auth::user())) {
<!-- 比赛导航 --> $tabs_info['backstage'] = [
<?php 'name' => UOJLocale::get('contests::contest backstage'),
$tabs_info = array( 'url' => '/contest/' . UOJContest::info('id') . '/backstage',
'dashboard' => array( ];
'name' => UOJLocale::get('contests::contest dashboard'), }
'url' => "/contest/{$contest['id']}" ?>
), <div class="mb-2">
'submissions' => array( <?= HTML::tablist($tabs_info, '', 'nav-pills') ?>
'name' => UOJLocale::get('contests::contest submissions'), </div>
'url' => "/contest/{$contest['id']}/submissions" <?php endif ?>
),
'standings' => array(
'name' => UOJLocale::get('contests::contest standings'),
'url' => "/contest/{$contest['id']}/standings"
),
);
if ($contest['cur_progress'] > CONTEST_TESTING) { <div class="card card-default mb-2">
$tabs_info['after_contest_standings'] = array( <div class="card-body">
'name' => UOJLocale::get('contests::after contest standings'),
'url' => "/contest/{$contest['id']}/after_contest_standings"
);
$tabs_info['self_reviews'] = array(
'name' => UOJLocale::get('contests::contest self reviews'),
'url' => "/contest/{$contest['id']}/self_reviews"
);
}
if (hasContestPermission(Auth::user(), $contest)) { <h1 class="text-center">
$tabs_info['backstage'] = array( <?php if (UOJContest::cur()) : ?>
'name' => UOJLocale::get('contests::contest backstage'), <?= UOJProblem::cur()->getTitle(['with' => 'letter']) ?>
'url' => "/contest/{$contest['id']}/backstage" <?php else : ?>
); <?= UOJProblem::cur()->getTitle(['with' => 'id']) ?>
} <?php endif ?>
?> </h1>
<div class="mb-2">
<?= HTML::tablist($tabs_info, '', 'nav-pills') ?>
</div>
<?php endif ?>
<div class="card card-default mb-2"> <hr />
<div class="card-body">
<h1 class="page-header text-center h2"> <h2 class="text-center"><?= UOJLocale::get('problems::accepted submissions') ?></h2>
<?php if ($contest): ?> <div class="text-end mb-2">
<?= $problem_letter ?>. <div class="btn-group btn-group-sm">
<?php else: ?> <a href="<?= UOJContext::requestURI() ?>" class="btn btn-secondary btn-xs <?= $submissions_sort_by_choice == 'time' ? 'active' : '' ?>" id="submissions-sort-by-run-time">
#<?= $problem['id'] ?>. <?= UOJLocale::get('problems::fastest') ?>
<?php endif ?> </a>
<?= $problem['title'] ?> <a href="<?= UOJContext::requestURI() ?>" class="btn btn-secondary btn-xs <?= $submissions_sort_by_choice == 'tot_size' ? 'active' : '' ?>" id="submissions-sort-by-code-length">
</h1> <?= UOJLocale::get('problems::shortest') ?>
</a>
</div>
</div>
<hr /> <script type="text/javascript">
$('#submissions-sort-by-run-time').click(function() {
$.cookie('submissions-sort-by-run-time', '');
$.removeCookie('submissions-sort-by-code-length');
});
$('#submissions-sort-by-code-length').click(function() {
$.cookie('submissions-sort-by-code-length', '');
$.removeCookie('submissions-sort-by-run-time');
});
</script>
<?php if ($contest && !hasContestPermission($myUser, $contest) && $contest['cur_progress'] <= CONTEST_IN_PROGRESS): ?> <?php
<h2 class="text-center text-muted">比赛尚未结束</h2> if ($submissions_sort_by_choice == 'time') {
<?php else: ?> $submid = 'best_ac_submissions.submission_id = submissions.id';
<h2 class="text-center h3"><?= UOJLocale::get('problems::accepted submissions') ?></h2> $orderby = 'order by best_ac_submissions.used_time, best_ac_submissions.used_memory, best_ac_submissions.tot_size, best_ac_submissions.submission_id';
<div class="text-end mb-2"> } else {
<div class="btn-group btn-group-sm"> $submid = 'best_ac_submissions.shortest_id = submissions.id';
<a href="<?= UOJContext::requestURI() ?>" class="btn btn-secondary btn-xs <?= $submissions_sort_by_choice == 'time' ? 'active' : '' ?>" id="submissions-sort-by-run-time"> $orderby = 'order by best_ac_submissions.shortest_tot_size, best_ac_submissions.shortest_used_time, best_ac_submissions.shortest_used_memory, best_ac_submissions.shortest_id';
<?= UOJLocale::get('problems::fastest') ?> }
</a>
<a href="<?= UOJContext::requestURI() ?>" class="btn btn-secondary btn-xs <?= $submissions_sort_by_choice == 'tot_size' ? 'active' : '' ?>" id="submissions-sort-by-code-length">
<?= UOJLocale::get('problems::shortest') ?>
</a>
</div>
</div>
<script type="text/javascript"> echoSubmissionsList([
$('#submissions-sort-by-run-time').click(function() { $submid,
$.cookie('submissions-sort-by-run-time', ''); "best_ac_submissions.problem_id" => UOJProblem::info('id')
$.removeCookie('submissions-sort-by-code-length'); ], $orderby, [
}); 'judge_time_hidden' => '',
$('#submissions-sort-by-code-length').click(function() { 'problem_hidden' => true,
$.cookie('submissions-sort-by-code-length', ''); 'table_name' => 'best_ac_submissions, submissions',
$.removeCookie('submissions-sort-by-run-time'); 'problem' => UOJProblem::cur(),
}); 'table_config' => [
</script> 'div_classes' => ['table-responsive', 'mb-3'],
'table_classes' => ['table', 'mb-0', 'text-center'],
]
], Auth::user());
?>
<?php <h2 class="text-center mt-4">
$table_config = [ <?= UOJLocale::get('problems::score distribution') ?>
'div_classes' => ['table-responsive', 'mb-3'], </h2>
'table_classes' => ['table', 'mb-0', 'text-center'], <div id="score-distribution-chart" style="height: 250px;"></div>
]; <script type="text/javascript">
?> new Morris.Bar({
element: 'score-distribution-chart',
data: <?= json_encode($data) ?>,
barColors: function(r, s, type) {
return getColOfScore(r.label);
},
xkey: 'score',
ykeys: ['count'],
labels: ['number'],
hoverCallback: function(index, options, content, row) {
var scr = row.score;
return '<div class="morris-hover-row-label">' + 'score: ' + scr + '</div>' +
'<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&amp;min_score=' + scr + '&amp;max_score=' + scr + '">' + 'number: ' + row.count + '</a>' + '</div>';
},
resize: true
});
</script>
<?php if ($submissions_sort_by_choice == 'time'): ?> <h2 class="text-center mt-4">
<?php echoSubmissionsList("best_ac_submissions.submission_id = submissions.id and best_ac_submissions.problem_id = {$problem['id']}", 'order by best_ac_submissions.used_time, best_ac_submissions.used_memory, best_ac_submissions.tot_size', array('problem_hidden' => '', 'judge_time_hidden' => '', 'table_name' => 'best_ac_submissions, submissions', 'table_config' => $table_config), $myUser); ?> <?= UOJLocale::get('problems::prefix sum of score distribution') ?>
<?php else: ?> </h2>
<?php echoSubmissionsList("best_ac_submissions.shortest_id = submissions.id and best_ac_submissions.problem_id = {$problem['id']}", 'order by best_ac_submissions.shortest_tot_size, best_ac_submissions.shortest_used_time, best_ac_submissions.shortest_used_memory', array('problem_hidden' => '', 'judge_time_hidden' => '', 'table_name' => 'best_ac_submissions, submissions', 'table_config' => $table_config), $myUser); ?> <div id="score-distribution-chart-pre" style="height: 250px;"></div>
<?php endif ?> <script type="text/javascript">
new Morris.Line({
element: 'score-distribution-chart-pre',
data: <?= json_encode($pre_data) ?>,
xkey: 'score',
ykeys: ['count'],
labels: ['number'],
lineColors: function(row, sidx, type) {
if (type == 'line') {
return '#0b62a4';
}
return getColOfScore(row.src.score / 100);
},
xLabelFormat: function(x) {
return (x.getTime() / 100).toString();
},
hoverCallback: function(index, options, content, row) {
var scr = row.score / 100;
return '<div class="morris-hover-row-label">' + 'score: &le;' + scr + '</div>' +
'<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&amp;max_score=' + scr + '">' + 'number: ' + row.count + '</a>' + '</div>';
},
resize: true
});
</script>
<h2 class="text-center h3 mt-4"> <h2 class="text-center mt-4">
<?= UOJLocale::get('problems::score distribution') ?> <?= UOJLocale::get('problems::suffix sum of score distribution') ?>
</h2> </h2>
<div id="score-distribution-chart" style="height: 250px;"></div> <div id="score-distribution-chart-suf" style="height: 250px;"></div>
<script type="text/javascript"> <script type="text/javascript">
new Morris.Bar({ new Morris.Line({
element: 'score-distribution-chart', element: 'score-distribution-chart-suf',
data: <?= json_encode($data) ?>, data: <?= json_encode($suf_data) ?>,
barColors: function(r, s, type) { xkey: 'score',
return getColOfScore(r.label); ykeys: ['count'],
}, labels: ['number'],
xkey: 'score', lineColors: function(row, sidx, type) {
ykeys: ['count'], if (type == 'line') {
labels: ['number'], return '#0b62a4';
hoverCallback: function(index, options, content, row) { }
var scr = row.score; return getColOfScore(row.src.score / 100);
return '<div class="morris-hover-row-label">' + 'score: ' + scr + '</div>' + },
'<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&amp;min_score=' + scr + '&amp;max_score=' + scr + '">' + 'number: ' + row.count + '</a>' + '</div>'; xLabelFormat: function(x) {
}, return (x.getTime() / 100).toString();
resize: true },
}); hoverCallback: function(index, options, content, row) {
</script> var scr = row.score / 100;
return '<div class="morris-hover-row-label">' + 'score: &ge;' + scr + '</div>' +
<h2 class="text-center h3 mt-4"> '<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&amp;min_score=' + scr + '">' + 'number: ' + row.count + '</a>' + '</div>';
<?= UOJLocale::get('problems::prefix sum of score distribution') ?> },
</h2> resize: true
<div id="score-distribution-chart-pre" style="height: 250px;"></div> });
<script type="text/javascript"> </script>
new Morris.Line({ </div>
element: 'score-distribution-chart-pre',
data: <?= json_encode($pre_data) ?>,
xkey: 'score',
ykeys: ['count'],
labels: ['number'],
lineColors: function(row, sidx, type) {
if (type == 'line') {
return '#0b62a4';
}
return getColOfScore(row.src.score / 100);
},
xLabelFormat: function(x) {
return (x.getTime() / 100).toString();
},
hoverCallback: function(index, options, content, row) {
var scr = row.score / 100;
return '<div class="morris-hover-row-label">' + 'score: &le;' + scr + '</div>' +
'<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&amp;max_score=' + scr + '">' + 'number: ' + row.count + '</a>' + '</div>';
},
resize: true
});
</script>
<h2 class="text-center h3 mt-4">
<?= UOJLocale::get('problems::suffix sum of score distribution') ?>
</h2>
<div id="score-distribution-chart-suf" style="height: 250px;"></div>
<script type="text/javascript">
new Morris.Line({
element: 'score-distribution-chart-suf',
data: <?= json_encode($suf_data) ?>,
xkey: 'score',
ykeys: ['count'],
labels: ['number'],
lineColors: function(row, sidx, type) {
if (type == 'line') {
return '#0b62a4';
}
return getColOfScore(row.src.score / 100);
},
xLabelFormat: function(x) {
return (x.getTime() / 100).toString();
},
hoverCallback: function(index, options, content, row) {
var scr = row.score / 100;
return '<div class="morris-hover-row-label">' + 'score: &ge;' + scr + '</div>' +
'<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&amp;min_score=' + scr + '">' + 'number: ' + row.count + '</a>' + '</div>';
},
resize: true
});
</script>
<?php endif // $contest && !hasContestPermission($myUser, $contest) && $contest['cur_progress'] <= CONTEST_IN_PROGRESS ?>
</div>
</div>
<!-- end left col -->
</div>
<!-- Right col -->
<aside class="col-lg-3 mt-3 mt-lg-0">
<?php if ($contest): ?>
<!-- Contest card -->
<div class="card card-default mb-2">
<div class="card-body">
<h3 class="h5 card-title text-center">
<a class="text-decoration-none text-body" href="/contest/<?= $contest['id'] ?>">
<?= $contest['name'] ?>
</a>
</h3>
<div class="card-text text-center text-muted">
<?php if ($contest['cur_progress'] <= CONTEST_IN_PROGRESS): ?>
<span id="contest-countdown"></span>
<?php else: ?>
<?= UOJLocale::get('contests::contest ended') ?>
<?php endif ?>
</div> </div>
</div>
<div class="card-footer bg-transparent">
比赛评价:<?= getClickZanBlock('C', $contest['id'], $contest['zan']) ?>
</div>
</div>
<?php if ($contest['cur_progress'] <= CONTEST_IN_PROGRESS): ?>
<script type="text/javascript">
$('#contest-countdown').countdown(<?= $contest['end_time']->getTimestamp() - UOJTime::$time_now->getTimestamp() ?>, function(){}, '1.75rem', false);
</script>
<?php endif ?>
<?php endif ?>
<div class="card card-default mb-2"> <!-- end left col -->
<ul class="nav nav-pills nav-fill flex-column" role="tablist"> </div>
<li class="nav-item text-start">
<a class="nav-link" role="tab" <!-- Right col -->
<?php if ($contest): ?> <aside class="col-lg-3 mt-3 mt-lg-0">
href="/contest/<?= $contest['id'] ?>/problem/<?= $problem['id'] ?>"
<?php else: ?> <?php if ($contest) : ?>
href="/problem/<?= $problem['id'] ?>" <!-- Contest card -->
<?php endif ?>> <div class="card card-default mb-2">
<i class="bi bi-journal-text"></i> <div class="card-body">
<?= UOJLocale::get('problems::statement') ?> <h3 class="h4 card-title text-center">
</a> <a class="text-decoration-none text-body" href="/contest/<?= $contest['id'] ?>">
</li> <?= $contest['name'] ?>
<?php if (!$contest || $contest['cur_progress'] >= CONTEST_FINISHED): ?> </a>
<li class="nav-item text-start"> </h3>
<a href="/problem/<?= $problem['id'] ?>/solutions" class="nav-link" role="tab"> <div class="card-text text-center text-muted">
<i class="bi bi-journal-bookmark"></i> <?php if ($contest['cur_progress'] <= CONTEST_IN_PROGRESS) : ?>
<?= UOJLocale::get('problems::solutions') ?> <span id="contest-countdown"></span>
</a> <?php else : ?>
</li> <?= UOJLocale::get('contests::contest ended') ?>
<?php endif ?>
</div>
</div>
<div class="card-footer bg-transparent">
比赛评价:<?= ClickZans::getBlock('C', $contest['id'], $contest['zan']) ?>
</div>
</div>
<?php if ($contest['cur_progress'] <= CONTEST_IN_PROGRESS) : ?>
<script type="text/javascript">
$('#contest-countdown').countdown(<?= $contest['end_time']->getTimestamp() - UOJTime::$time_now->getTimestamp() ?>, function() {}, '1.75rem', false);
</script>
<?php endif ?>
<?php endif ?> <?php endif ?>
<li class="nav-item text-start">
<a class="nav-link active" href="#">
<i class="bi bi-graph-up"></i>
<?= UOJLocale::get('problems::statistics') ?>
</a>
</li>
<?php if (hasProblemPermission($myUser, $problem)): ?>
<li class="nav-item text-start">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab">
<i class="bi bi-sliders"></i>
<?= UOJLocale::get('problems::manage') ?>
</a>
</li>
<?php endif ?>
</ul>
<div class="card-footer bg-transparent">
评价:<?= getClickZanBlock('P', $problem['id'], $problem['zan']) ?>
</div>
</div>
<?php uojIncludeView('sidebar', array()); ?> <div class="card card-default mb-2">
<ul class="nav nav-pills nav-fill flex-column" role="tablist">
<li class="nav-item text-start">
<a class="nav-link" role="tab" <?php if ($contest) : ?> href="/contest/<?= $contest['id'] ?>/problem/<?= $problem['id'] ?>" <?php else : ?> href="/problem/<?= $problem['id'] ?>" <?php endif ?>>
<i class="bi bi-journal-text"></i>
<?= UOJLocale::get('problems::statement') ?>
</a>
</li>
<?php if (!$contest || $contest['cur_progress'] >= CONTEST_FINISHED) : ?>
<li class="nav-item text-start">
<a href="/problem/<?= $problem['id'] ?>/solutions" class="nav-link" role="tab">
<i class="bi bi-journal-bookmark"></i>
<?= UOJLocale::get('problems::solutions') ?>
</a>
</li>
<?php endif ?>
<li class="nav-item text-start">
<a class="nav-link active" href="#">
<i class="bi bi-graph-up"></i>
<?= UOJLocale::get('problems::statistics') ?>
</a>
</li>
<?php if (hasProblemPermission($myUser, $problem)) : ?>
<li class="nav-item text-start">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab">
<i class="bi bi-sliders"></i>
<?= UOJLocale::get('problems::manage') ?>
</a>
</li>
<?php endif ?>
</ul>
<div class="card-footer bg-transparent">
评价:<?= ClickZans::getBlock('P', $problem['id'], $problem['zan']) ?>
</div>
</div>
<!-- End right col --> <?php uojIncludeView('sidebar') ?>
</aside>
<!-- End right col -->
</aside>
</div> </div>
<?php if ($contest && $contest['cur_progress'] <= CONTEST_IN_PROGRESS): ?>
<script type="text/javascript">
checkContestNotice(<?= $contest['id'] ?>, '<?= UOJTime::$time_now_str ?>');
</script>
<?php endif ?>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,44 +1,39 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin();
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { $config = [
become403Page(); 'page_len' => 50,
} 'div_classes' => ['card', 'mb-3'],
'table_classes' => ['table', 'uoj-table', 'mb-0', 'text-center'],
'card' => true
];
$config = [ if (isset($_GET['type']) && $_GET['type'] == 'accepted') {
'page_len' => 50, $config['by_accepted'] = true;
'div_classes' => ['card', 'mb-3'], $title = UOJLocale::get('top solver');
'table_classes' => ['table', 'uoj-table', 'mb-0', 'text-center'], } else {
]; become404Page();
}
if (isset($_GET['type']) && $_GET['type'] == 'accepted') { ?>
$config['by_accepted'] = true;
$title = UOJLocale::get('top solver');
} else {
become404Page();
}
?>
<?php echoUOJPageHeader($title) ?> <?php echoUOJPageHeader($title) ?>
<div class="row"> <div class="row">
<!-- left col --> <!-- left col -->
<div class="col-lg-9"> <div class="col-lg-9">
<h1 class="h2"><?= $title ?></h1> <h1><?= $title ?></h1>
<?php echoRanklist($config) ?> <?php UOJRanklist::printHTML($config) ?>
</div> </div>
<!-- end left col --> <!-- end left col -->
<!-- right col --> <!-- right col -->
<aside class="col-lg-3 mt-3 mt-lg-0"> <aside class="col-lg-3 mt-3 mt-lg-0">
<?php uojIncludeView('sidebar', array()) ?> <?php uojIncludeView('sidebar') ?>
</aside> </aside>
<!-- end right col --> <!-- end right col -->
</div> </div>

View File

@ -1,194 +1,196 @@
<?php <?php
if (!UOJConfig::$data['switch']['open-register'] && DB::selectCount("SELECT COUNT(*) FROM user_info")) { requireLib('bootstrap5');
become404Page(); requireLib('md5');
use Gregwar\Captcha\PhraseBuilder;
if (!UOJConfig::$data['switch']['open-register'] && DB::selectCount("SELECT COUNT(*) FROM user_info")) {
become404Page();
}
function handleRegisterPost() {
if (!crsf_check()) {
return '页面已过期';
} }
function handleRegisterPost() { if (!isset($_SESSION['phrase']) || !PhraseBuilder::comparePhrases($_SESSION['phrase'], $_POST['captcha'])) {
if (!crsf_check()) { return "bad_captcha";
return '页面已过期';
}
if (!isset($_POST['username'])) {
return "无效表单";
}
if (!isset($_POST['password'])) {
return "无效表单";
}
if (!isset($_POST['email'])) {
return "无效表单";
}
if (!UOJConfig::$data['switch']['open-register'] && DB::selectCount("SELECT COUNT(*) FROM user_info")) {
return "只有首位用户可以注册。";
}
$username = $_POST['username'];
$password = $_POST['password'];
$email = $_POST['email'];
if (!validateUsername($username)) {
return "失败:无效用户名。";
}
if (queryUser($username)) {
return "失败:用户名已存在。";
}
if (!validatePassword($password)) {
return "失败:无效密码。";
}
if (!validateEmail($email)) {
return "失败:无效电子邮箱。";
}
$password = getPasswordToStore($password, $username);
$esc_email = DB::escape($email);
$svn_pw = uojRandString(10);
if (!DB::selectCount("SELECT COUNT(*) FROM user_info")) {
DB::query("insert into user_info (username, email, password, svn_password, register_time, usergroup) values ('$username', '$esc_email', '$password', '$svn_pw', now(), 'S')");
} else {
DB::query("insert into user_info (username, email, password, svn_password, register_time) values ('$username', '$esc_email', '$password', '$svn_pw', now())");
}
return "欢迎你!" . $username . ",你已成功注册。";
} }
if (isset($_POST['register'])) { if (!isset($_POST['username']) || !isset($_POST['password']) || !isset($_POST['email'])) {
echo handleRegisterPost(); return "无效表单";
die();
} elseif (isset($_POST['check_username'])) {
$username = $_POST['username'];
if (validateUsername($username) && !queryUser($username)) {
echo '{"ok" : true}';
} else {
echo '{"ok" : false}';
}
die();
} }
?>
<?php try {
$REQUIRE_LIB['md5'] = ''; $user = UOJUser::register([
$REQUIRE_LIB['dialog'] = ''; 'username' => UOJRequest::post('username'),
?> 'password' => UOJRequest::post('password'),
'email' => UOJRequest::post('email')
]);
} catch (UOJInvalidArgumentException $e) {
return "失败:" . $e->getMessage();
}
return "欢迎你!" . $user['username'] . ",你已成功注册。";
}
if (isset($_POST['register'])) {
echo handleRegisterPost();
unset($_SESSION['phrase']);
die();
} elseif (isset($_POST['check_username'])) {
$username = $_POST['username'];
if (validateUsername($username) && !queryUser($username)) {
die('{"ok": true}');
} else {
die('{"ok": false}');
}
}
?>
<?php echoUOJPageHeader(UOJLocale::get('register')) ?> <?php echoUOJPageHeader(UOJLocale::get('register')) ?>
<h2 class="page-header"><?= UOJLocale::get('register') ?></h2>
<form id="form-register" class="form-horizontal">
<div id="div-email" class="form-group"> <form id="form-register" class="card mw-100 mx-auto" style="width:600px">
<label for="input-email" class="col-sm-2 control-label"><?= UOJLocale::get('email') ?></label> <div class="card-body">
<div class="col-sm-3"> <h1 class="card-title text-center mb-3">
<?= UOJLocale::get('register') ?>
</h1>
<div id="div-email" class="form-group">
<label for="input-email" class="form-label"><?= UOJLocale::get('email') ?></label>
<input type="email" class="form-control" id="input-email" name="email" placeholder="<?= UOJLocale::get('enter your email') ?>" maxlength="50" /> <input type="email" class="form-control" id="input-email" name="email" placeholder="<?= UOJLocale::get('enter your email') ?>" maxlength="50" />
<span class="help-block" id="help-email"></span> <span class="help-block" id="help-email"></span>
</div> </div>
</div> <div id="div-username" class="form-group">
<div id="div-username" class="form-group"> <label for="input-username" class="form-label"><?= UOJLocale::get('username') ?></label>
<label for="input-username" class="col-sm-2 control-label"><?= UOJLocale::get('username') ?></label>
<div class="col-sm-3">
<input type="text" class="form-control" id="input-username" name="username" placeholder="<?= UOJLocale::get('enter your username') ?>" maxlength="20" /> <input type="text" class="form-control" id="input-username" name="username" placeholder="<?= UOJLocale::get('enter your username') ?>" maxlength="20" />
<span class="help-block" id="help-username"></span> <span class="help-block" id="help-username"></span>
</div> </div>
</div> <div id="div-password" class="form-group">
<div id="div-password" class="form-group"> <label for="input-password" class="form-label"><?= UOJLocale::get('password') ?></label>
<label for="input-password" class="col-sm-2 control-label"><?= UOJLocale::get('password') ?></label>
<div class="col-sm-3">
<input type="password" class="form-control" id="input-password" name="password" placeholder="<?= UOJLocale::get('enter your password') ?>" maxlength="20" /> <input type="password" class="form-control" id="input-password" name="password" placeholder="<?= UOJLocale::get('enter your password') ?>" maxlength="20" />
<input type="password" class="form-control top-buffer-sm" id="input-confirm_password" placeholder="<?= UOJLocale::get('re-enter your password') ?>" maxlength="20" /> <input type="password" class="form-control mt-2" id="input-confirm_password" placeholder="<?= UOJLocale::get('re-enter your password') ?>" maxlength="20" />
<span class="help-block" id="help-password"></span> <span class="help-block" id="help-password"></span>
</div> </div>
</div> <div id="div-captcha" class="form-group">
<div class="form-group"> <label for="input-captcha"><?= UOJLocale::get('verification code') ?></label>
<div class="col-sm-offset-2 col-sm-3"> <div class="input-group">
<button type="submit" id="button-submit" class="btn btn-secondary"><?= UOJLocale::get('submit') ?></button> <input type="text" class="form-control" id="input-captcha" name="captcha" placeholder="<?= UOJLocale::get('enter verification code') ?>" maxlength="20" />
<span class="input-group-text p-0">
<img id="captcha" class="col w-100 h-100" src="/captcha">
</span>
</div>
</div>
<div class="text-center">
<button type="submit" id="button-submit" class="btn btn-primary"><?= UOJLocale::get('submit') ?></button>
</div> </div>
</div> </div>
</form> </form>
<script type="text/javascript"> <div id="dialog" class="modal fade" tabindex="-1">
function checkUsernameNotInUse() { <div class="modal-dialog">
var ok = false; <div class="modal-content">
$.ajax({ <div class="modal-header">
url : '/register', <h5 class="modal-title" id="modal-title"></h5>
type : 'POST', <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
dataType : 'json', </div>
async : false, <div class="modal-body">
<p id="modal-text"></p>
data : { </div>
check_username : '', <div class="modal-footer">
username : $('#input-username').val() <button type="button" class="btn btn-primary" data-bs-dismiss="modal" aria-label="Close">好的</button>
}, </div>
success : function(data) { </div>
ok = data.ok; </div>
}, </div>
error : function(XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.responseText);
ok = false;
}
});
return ok;
}
function validateRegisterPost() {
var ok = true;
ok &= getFormErrorAndShowHelp('email', validateEmail);
ok &= getFormErrorAndShowHelp('username', function(str) {
var err = validateUsername(str);
if (err)
return err;
if (!checkUsernameNotInUse())
return '该用户名已被人使用了。';
return '';
})
ok &= getFormErrorAndShowHelp('password', validateSettingPassword);
return ok;
}
function submitRegisterPost() { <script type="text/javascript">
if (!validateRegisterPost()) { function refreshCaptcha() {
return; var timestamp = new Date().getTime();
$("#captcha").attr("src", "/captcha" + '?' + timestamp);
} }
$.post('/register', { function checkUsernameNotInUse() {
_token : "<?= crsf_token() ?>", var ok = false;
register : '', $.ajax({
username : $('#input-username').val(), url: '/register',
email : $('#input-email').val(), type: 'POST',
password : md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>") dataType: 'json',
}, function(msg) { async: false,
if (/^欢迎你!/.test(msg)) {
BootstrapDialog.show({ data: {
title : '注册成功', check_username: '',
message : msg, username: $('#input-username').val()
type : BootstrapDialog.TYPE_SUCCESS, },
buttons: [{ success: function(data) {
label: '好的', ok = data.ok;
action: function(dialog) { },
dialog.close(); error: function(XMLHttpRequest, textStatus, errorThrown) {
} alert(XMLHttpRequest.responseText);
}], ok = false;
onhidden : function(dialog) { }
var prevUrl = document.referrer; });
if (!prevUrl) { return ok;
prevUrl = '/'; }
};
window.location.href = prevUrl; function validateRegisterPost() {
} var ok = true;
}); ok &= getFormErrorAndShowHelp('email', validateEmail);
} else { ok &= getFormErrorAndShowHelp('username', function(str) {
BootstrapDialog.show({ var err = validateUsername(str);
title : '注册失败', if (err)
message : msg, return err;
type : BootstrapDialog.TYPE_DANGER, if (!checkUsernameNotInUse())
buttons: [{ return '该用户名已被人使用了。';
label: '好的', return '';
action: function(dialog) { })
dialog.close(); ok &= getFormErrorAndShowHelp('password', validateSettingPassword);
} return ok;
}], }
});
function submitRegisterPost() {
if (!validateRegisterPost()) {
return;
} }
$.post('/register', {
_token: "<?= crsf_token() ?>",
register: '',
username: $('#input-username').val(),
email: $('#input-email').val(),
password: md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>"),
captcha: $('#input-captcha').val(),
}, function(msg) {
if (/^欢迎你!/.test(msg)) {
var prevUrl = document.referrer;
if (!prevUrl) {
prevUrl = '/';
};
$('#modal-title').html('注册成功');
$('#modal-text').html(msg);
$('#dialog')
.modal('show')
.on('hidden.bs.modal', function() {
window.location.href = prevUrl;
});
refreshCaptcha();
} else {
$('#modal-title').html('注册失败');
$('#modal-text').html(msg);
$('#dialog').modal('show');
refreshCaptcha();
}
});
}
$(document).ready(function() {
refreshCaptcha();
$('#captcha').click(refreshCaptcha);
$('#form-register').submit(function(e) {
submitRegisterPost();
return false;
});
}); });
}
$(document).ready(function() {
$('#form-register').submit(function(e) {
submitRegisterPost();
return false;
});
});
</script> </script>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,18 +1,11 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requireLib('calendar_heatmap'); requireLib('calendar_heatmap');
requirePHPLib('form');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin(); ?>
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) {
become403Page();
}
?>
<?php echoUOJPageHeader('关于我') ?> <?php echoUOJPageHeader('关于我') ?>
<?php uojIncludeView('user-info', array('user' => UOJContext::user(), 'is_blog_aboutme' => '')) ?> <?php uojIncludeView('user-info', ['user' => UOJUserBlog::user(), 'is_blog_aboutme' => true]) ?>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,154 +1,171 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requireLib('mathjax'); requireLib('mathjax');
requireLib('hljs'); requireLib('hljs');
requirePHPLib('form'); requirePHPLib('form');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin();
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { $blogs_conds = [
become403Page(); "poster" => UOJUserBlog::id(),
} ];
$blogs_cond = "poster = '".UOJContext::userid()."'"; if (!UOJUserBlog::userCanManage(Auth::user())) {
if (!UOJContext::hasBlogPermission()) { $blogs_conds["is_hidden"] = false;
$blogs_cond .= " and is_hidden = false"; }
}
$display_blogs_conds = $blogs_conds;
$display_blogs_cond = $blogs_cond;
if (isset($_GET['tag'])) {
if (isset($_GET['tag'])) { $blog_tag_required = $_GET['tag'];
$blog_tag_required = $_GET['tag'];
$display_blogs_cond .= " and '".DB::escape($blog_tag_required)."' in (select tag from blogs_tags where blogs_tags.blog_id = blogs.id)"; $display_blogs_conds[] = [
} else { DB::rawvalue($blog_tag_required), "in", DB::rawbracket([
$blog_tag_required = null; "select tag from blogs_tags",
} "where", ["blogs_tags.blog_id" => DB::raw("blogs.id")]
])
$blogs_pag = new Paginator(array( ];
'col_names' => array('*'), } else {
'table_name' => 'blogs', $blog_tag_required = null;
'cond' => $display_blogs_cond, }
'tail' => 'order by post_time desc',
'page_len' => 15 $blogs_pag = new Paginator([
)); 'col_names' => ['*'],
'table_name' => 'blogs',
$all_tags = DB::selectAll("select distinct tag from blogs_tags where blog_id in (select id from blogs where $blogs_cond)"); 'cond' => $display_blogs_conds,
?> 'tail' => 'order by post_time desc',
'page_len' => 15
]);
$all_tags = DB::selectAll([
"select distinct tag from blogs_tags",
"where", [
[
"blog_id", "in", DB::rawbracket([
"select id from blogs",
"where", $blogs_conds
])
]
]
]);
?>
<?php echoUOJPageHeader('日志') ?> <?php echoUOJPageHeader('日志') ?>
<div class="row"> <div class="row">
<div class="col-md-9"> <div class="col-md-9">
<?php if (!$blog_tag_required): ?> <?php if (!$blog_tag_required) : ?>
<?php if ($blogs_pag->isEmpty()): ?> <?php if ($blogs_pag->isEmpty()) : ?>
<div class="text-muted">此人很懒,什么博客也没留下。</div> <div class="text-muted">此人很懒,什么博客也没留下。</div>
<?php else: ?> <?php else : ?>
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<table class="table uoj-table"> <table class="table uoj-table">
<thead> <thead>
<tr> <tr>
<th>标题</th> <th>标题</th>
<th style="width: 20%">发表时间</th> <th style="width: 20%">发表时间</th>
<th class="text-center" style="width: 100px">评价</th> <th class="text-center" style="width: 100px">评价</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php $cnt = 0 ?> <?php $cnt = 0; ?>
<?php foreach ($blogs_pag->get() as $blog): ?> <?php foreach ($blogs_pag->get() as $blog_info) : ?>
<?php $cnt++ ?> <?php
<tr> $blog = new UOJBlog($blog_info);
<td> $cnt++;
<?php if ($blog['is_hidden']): ?> ?>
<span class="text-danger">[隐藏]</span> <tr>
<?php endif ?> <td>
<?= getBlogLink($blog['id']) ?> <?php if ($blog->info['is_hidden']) : ?>
<?php foreach (queryBlogTags($blog['id']) as $tag): ?> <span class="text-danger">[隐藏]</span>
<?php echoBlogTag($tag) ?> <?php endif ?>
<?php endforeach ?> <?= $blog->getLink() ?>
</td> <?php foreach ($blog->queryTags() as $tag) : ?>
<td><?= $blog['post_time'] ?></td> <?php echoBlogTag($tag) ?>
<td><?= getClickZanBlock('B', $blog['id'], $blog['zan'], null, false) ?></td> <?php endforeach ?>
</tr> </td>
<?php endforeach ?> <td><?= $blog->info['post_time'] ?></td>
</tbody> <td><?= ClickZans::getBlock('B', $blog->info['id'], $blog->info['zan'], null, false) ?></td>
</table> </tr>
<?php endforeach ?>
</tbody>
</table>
</div>
<div class="card-footer bg-transparent text-end text-muted">
<?= $blogs_pag->cur_start + 1 ?> - <?= $blogs_pag->cur_start + $cnt ?> 篇,共 <?= $blogs_pag->n_rows ?> 篇博客
</div>
</div> </div>
<div class="card-footer bg-transparent text-end text-muted">
<?= $blogs_pag->cur_start + 1 ?> - <?= $blogs_pag->cur_start + $cnt ?> 篇,共 <?= $blogs_pag->n_rows ?> 篇博客
</div>
</div>
<?php endif ?> <?php endif ?>
<?php else: ?> <?php else : ?>
<?php if ($blogs_pag->isEmpty()): ?> <?php if ($blogs_pag->isEmpty()) : ?>
<div class="alert alert-danger"> <div class="alert alert-danger">
没有找到包含 <?= HTML::escape($blog_tag_required) ?>” 标签的博客: 没有找到包含 <?= HTML::escape($blog_tag_required) ?>” 标签的博客:
</div> </div>
<?php else: ?> <?php else : ?>
<div class="alert alert-success"> <div class="alert alert-success">
共找到 <?= $blogs_pag->n_rows ?> 篇包含 “<?= HTML::escape($blog_tag_required) ?>” 标签的博客: 共找到 <?= $blogs_pag->n_rows ?> 篇包含 “<?= HTML::escape($blog_tag_required) ?>” 标签的博客:
</div> </div>
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<table class="table uoj-table mb-0"> <table class="table uoj-table mb-0">
<thead> <thead>
<tr> <tr>
<th>标题</th> <th>标题</th>
<th style="width: 20%">发表时间</th> <th style="width: 20%">发表时间</th>
<th class="text-center" style="width: 180px">评价</th> <th class="text-center" style="width: 100px">评价</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($blogs_pag->get() as $blog): ?> <?php foreach ($blogs_pag->get() as $blog_info) : ?>
<tr> <?php $blog = new UOJBlog($blog_info); ?>
<td> <tr>
<?php if ($blog['is_hidden']): ?> <td>
<span class="text-danger">[隐藏]</span> <?php if ($blog->info['is_hidden']) : ?>
<?php endif ?> <span class="text-danger">[隐藏]</span>
<?= getBlogLink($blog['id']) ?> <?php endif ?>
<?php foreach (queryBlogTags($blog['id']) as $tag): ?> <?= $blog->getLink() ?>
<?php echoBlogTag($tag) ?> <?php foreach ($blog->queryTags() as $tag) : ?>
<?php endforeach ?> <?php echoBlogTag($tag) ?>
</td> <?php endforeach ?>
<td><?= $blog['post_time'] ?></td> </td>
<td><?= getClickZanBlock('B', $blog['id'], $blog['zan']) ?></td> <td><?= $blog->info['post_time'] ?></td>
</tr> <td><?= ClickZans::getBlock('B', $blog->info['id'], $blog->info['zan'], null, false) ?></td>
<?php endforeach ?> </tr>
</tbody> <?php endforeach ?>
</table> </tbody>
</div> </table>
</div> </div>
</div>
<?php endif ?> <?php endif ?>
<?php endif ?> <?php endif ?>
<div class="text-center mt-3"> <div class="text-center mt-3">
<?= $blogs_pag->pagination() ?> <?= $blogs_pag->pagination() ?>
</div> </div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<?php if (UOJContext::hasBlogPermission()): ?> <?php if (UOJUserBlog::userCanManage(Auth::user())) : ?>
<div class="btn-group d-flex"> <div class="btn-group d-flex">
<a href="<?= HTML::blog_url(UOJContext::userid(), '/post/new/write') ?>" class="btn btn-primary"> <a href="<?= HTML::blog_url(UOJUserBlog::id(), '/post/new/write') ?>" class="btn btn-primary">
<i class="bi bi-pencil-square"></i> <i class="bi bi-pencil-square"></i>
写新博客 写新博客
</a> </a>
<a href="<?= HTML::blog_url(UOJContext::userid(), '/slide/new/write') ?>" class="btn btn-primary"> <a href="<?= HTML::blog_url(UOJUserBlog::id(), '/slide/new/write') ?>" class="btn btn-primary">
<i class="bi bi-file-earmark-slides"></i> <i class="bi bi-file-earmark-slides"></i>
写新幻灯片 写新幻灯片
</a> </a>
</div> </div>
<?php endif ?> <?php endif ?>
<div class="card border-info mt-3"> <div class="card border-info mt-3">
<div class="card-header bg-info">标签</div> <div class="card-header bg-info">标签</div>
<div class="card-body"> <div class="card-body">
<?php if ($all_tags): ?> <?php if ($all_tags) : ?>
<?php foreach ($all_tags as $tag): ?> <?php foreach ($all_tags as $tag) : ?>
<?php echoBlogTag($tag['tag']) ?> <?php echoBlogTag($tag['tag']) ?>
<?php endforeach ?> <?php endforeach ?>
<?php else: ?> <?php else : ?>
<div class="text-muted">暂无</div> <div class="text-muted">暂无</div>
<?php endif ?> <?php endif ?>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,220 +1,261 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requireLib('mathjax'); requireLib('mathjax');
requireLib('hljs'); requireLib('hljs');
requirePHPLib('form'); requirePHPLib('form');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { UOJBlog::init(UOJRequest::get('id')) || UOJResponse::page404();
redirectToLogin(); UOJBlog::cur()->belongsToUserBlog() || UOJResponse::page404();
UOJBlog::cur()->userCanView(Auth::user()) || UOJResponse::page403();
$blog = UOJBlog::info();
function getCommentContentToDisplay($comment) {
if (!$comment['is_hidden']) {
return $comment['content'];
} else {
return '<span class="text-muted">【' . HTML::escape($comment['reason_to_hide']) . '】</span>';
} }
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { if (!Auth::check()) {
become403Page(); redirectToLogin();
} }
if (!isset($_GET['id']) || !validateUInt($_GET['id']) || !($blog = queryBlog($_GET['id'])) || !UOJContext::isHis($blog)) { $solutions = DB::selectAll("select * from problems_solutions where blog_id = {$blog['id']}");
become404Page(); if ($solutions) {
} foreach ($solutions as $solution) {
if ($blog['is_hidden'] && !UOJContext::hasBlogPermission()) { $problem = queryProblemBrief($solution['problem_id']);
become403Page();
}
$solutions = DB::selectAll("select * from problems_solutions where blog_id = {$blog['id']}"); if (!hasProblemPermission($myUser, $problem) && isRegisteredRunningContestProblem($myUser, $problem)) {
if ($solutions) { become403Page();
foreach ($solutions as $solution) {
$problem = queryProblemBrief($solution['problem_id']);
if (!hasProblemPermission($myUser, $problem) && isRegisteredRunningContestProblem($myUser, $problem)) {
become403Page();
}
} }
} }
}
$comment_form = new UOJForm('comment');
$comment_form->addVTextArea('comment', '内容', '',
function($comment) {
global $myUser;
if ($myUser == null) {
return '请先登录';
}
if (!$comment) {
return '评论不能为空';
}
if (strlen($comment) > 1000) {
return '不能超过1000个字节';
}
return '';
},
null
);
$comment_form->handle = function() {
global $myUser, $blog, $comment_form;
$comment = HTML::escape($_POST['comment']);
list($comment, $referrers) = uojHandleAtSign($comment, "/post/{$blog['id']}");
$esc_comment = DB::escape($comment);
DB::insert("insert into blogs_comments (poster, blog_id, content, reply_id, post_time, zan) values ('{$myUser['username']}', '{$blog['id']}', '$esc_comment', 0, now(), 0)");
$comment_id = DB::insert_id();
$rank = DB::selectCount("select count(*) from blogs_comments where blog_id = {$blog['id']} and reply_id = 0 and id < {$comment_id}");
$page = floor($rank / 20) + 1;
$uri = getLongTablePageUri($page) . '#' . "comment-{$comment_id}";
$user_link = getUserLink($myUser['username']);
foreach ($referrers as $referrer) { $comment_form = new UOJBs4Form('comment');
$content = $user_link . ' 在博客 ' . $blog['title'] . ' 的评论里提到你:<a href="' . $uri . '">点击此处查看</a>'; $comment_form->addVTextArea(
sendSystemMsg($referrer, '有人提到你', $content); 'comment',
'内容',
'',
function ($comment) {
if (Auth::check()) {
return '请先登录';
} }
if (!$comment) {
if ($blog['poster'] !== $myUser['username']) { return '评论不能为空';
$content = $user_link . ' 回复了您的博客 ' . $blog['title'] . ' <a href="' . $uri . '">点击此处查看</a>';
sendSystemMsg($blog['poster'], '博客新回复通知', $content);
} }
if (strlen($comment) > 1000) {
$comment_form->succ_href = getLongTablePageRawUri($page); return '不能超过1000个字节';
};
$comment_form->ctrl_enter_submit = true;
$comment_form->runAtServer();
$reply_form = new UOJForm('reply');
$reply_form->addHidden('reply_id', '0',
function($reply_id, &$vdata) {
global $blog;
if (!validateUInt($reply_id) || $reply_id == 0) {
return '您要回复的对象不存在';
}
$comment = queryBlogComment($reply_id);
if (!$comment || $comment['blog_id'] != $blog['id']) {
return '您要回复的对象不存在';
}
$vdata['parent'] = $comment;
return '';
},
null
);
$reply_form->addVTextArea('reply_comment', '内容', '',
function($comment) {
global $myUser;
if ($myUser == null) {
return '请先登录';
}
if (!$comment) {
return '评论不能为空';
}
if (strlen($comment) > 140) {
return '不能超过140个字节';
}
return '';
},
null
);
$reply_form->handle = function(&$vdata) {
global $myUser, $blog, $reply_form;
$comment = HTML::escape($_POST['reply_comment']);
list($comment, $referrers) = uojHandleAtSign($comment, "/post/{$blog['id']}");
$reply_id = $_POST['reply_id'];
$esc_comment = DB::escape($comment);
DB::insert("insert into blogs_comments (poster, blog_id, content, reply_id, post_time, zan) values ('{$myUser['username']}', '{$blog['id']}', '$esc_comment', $reply_id, now(), 0)");
$comment_id = DB::insert_id();
$rank = DB::selectCount("select count(*) from blogs_comments where blog_id = {$blog['id']} and reply_id = 0 and id < {$reply_id}");
$page = floor($rank / 20) + 1;
$uri = getLongTablePageUri($page) . '#' . "comment-{$reply_id}";
$user_link = getUserLink($myUser['username']);
foreach ($referrers as $referrer) {
$content = $user_link . ' 在博客 ' . $blog['title'] . ' 的评论里提到你:<a href="' . $uri . '">点击此处查看</a>';
sendSystemMsg($referrer, '有人提到你', $content);
} }
return '';
$parent = $vdata['parent']; },
$notified = array(); null
if ($parent['poster'] !== $myUser['username']) { );
$notified[] = $parent['poster']; $comment_form->handle = function () {
$content = $user_link . ' 回复了您在博客 ' . $blog['title'] . ' 下的评论:<a href="' . $uri . '">点击此处查看</a>'; global $myUser, $blog, $comment_form;
sendSystemMsg($parent['poster'], '评论新回复通知', $content); $comment = HTML::escape($_POST['comment']);
list($comment, $referrers) = uojHandleAtSign($comment, "/post/{$blog['id']}");
DB::insert([
"insert into blogs_comments",
"(poster, blog_id, content, reply_id, post_time)",
"values", DB::tuple([Auth::id(), $blog['id'], $comment, 0, DB::now()])
]);
$comment_id = DB::insert_id();
$rank = DB::selectCount([
"select count(*) from blogs_comments",
"where", [
"blog_id" => $blog['id'],
"reply_id" => 0,
["id", "<", $comment_id]
]
]);
$page = floor($rank / 20) + 1;
$uri = getLongTablePageUri($page) . '#' . "comment-{$comment_id}";
$user_link = getUserLink($myUser['username']);
foreach ($referrers as $referrer) {
$content = $user_link . ' 在博客 ' . $blog['title'] . ' 的评论里提到你:<a href="' . $uri . '">点击此处查看</a>';
sendSystemMsg($referrer, '有人提到你', $content);
}
if ($blog['poster'] !== Auth::id()) {
$content = $user_link . ' 回复了您的博客 ' . $blog['title'] . ' <a href="' . $uri . '">点击此处查看</a>';
sendSystemMsg($blog['poster'], '博客新回复通知', $content);
}
UOJBlog::cur()->updateActiveTime();
$comment_form->succ_href = getLongTablePageRawUri($page);
};
$comment_form->ctrl_enter_submit = true;
$comment_form->runAtServer();
$reply_form = new UOJBs4Form('reply');
$reply_form->addHidden(
'reply_id',
'0',
function ($reply_id, &$vdata) {
global $blog;
if (!validateUInt($reply_id) || $reply_id == 0) {
return '您要回复的对象不存在';
} }
if ($blog['poster'] !== $myUser['username'] && !in_array($blog['poster'], $notified)) { $comment = UOJBlogComment::query($reply_id);
$notified[] = $blog['poster']; if (!$comment || $comment['blog_id'] != $blog['id']) {
$content = $user_link . ' 回复了您的博客 ' . $blog['title'] . ' <a href="' . $uri . '">点击此处查看</a>'; return '您要回复的对象不存在';
sendSystemMsg($blog['poster'], '博客新回复通知', $content);
} }
$vdata['parent'] = $comment;
$reply_form->succ_href = getLongTablePageRawUri($page); return '';
}; },
$reply_form->ctrl_enter_submit = true; null
);
$reply_form->runAtServer(); $reply_form->addVTextArea(
'reply_comment',
$comments_pag = new Paginator(array( '内容',
'col_names' => array('*'), '',
'table_name' => 'blogs_comments', function ($comment) {
'cond' => 'blog_id = ' . $blog['id'] . ' and reply_id = 0', if (!Auth::check()) {
'tail' => 'order by id asc', return '请先登录';
'page_len' => 20 }
)); if (!$comment) {
?> return '评论不能为空';
}
if (strlen($comment) > 140) {
return '不能超过140个字节';
}
return '';
},
null
);
$reply_form->handle = function (&$vdata) {
global $myUser, $blog, $reply_form;
$comment = HTML::escape($_POST['reply_comment']);
list($comment, $referrers) = uojHandleAtSign($comment, "/post/{$blog['id']}");
$reply_id = $_POST['reply_id'];
DB::insert([
"insert into blogs_comments",
"(poster, blog_id, content, reply_id, post_time)",
"values", DB::tuple([Auth::id(), $blog['id'], $comment, $reply_id, DB::now()])
]);
$comment_id = DB::insert_id();
$rank = DB::selectCount([
"select count(*) from blogs_comments",
"where", [
"blog_id" => $blog['id'],
"reply_id" => 0,
["id", "<", $reply_id]
]
]);
$page = floor($rank / 20) + 1;
$uri = getLongTablePageUri($page) . '#' . "comment-{$reply_id}";
$user_link = getUserLink($myUser['username']);
foreach ($referrers as $referrer) {
$content = $user_link . ' 在博客 ' . $blog['title'] . ' 的评论里提到你:<a href="' . $uri . '">点击此处查看</a>';
sendSystemMsg($referrer, '有人提到你', $content);
}
$parent = $vdata['parent'];
$notified = [];
if ($parent->info['poster'] !== Auth::id()) {
$notified[] = $parent->info['poster'];
$content = $user_link . ' 回复了您在博客 ' . $blog['title'] . ' 下的评论 <a href="' . $uri . '">点击此处查看</a>';
sendSystemMsg($parent->info['poster'], '评论新回复通知', $content);
}
if ($blog['poster'] !== Auth::id() && !in_array($blog['poster'], $notified)) {
$notified[] = $blog['poster'];
$content = $user_link . '回复了您的博客 ' . $blog['title'] . ' <a href="' . $uri . '">点击此处查看</a>';
sendSystemMsg($blog['poster'], '博客新回复通知', $content);
}
UOJBlog::cur()->updateActiveTime();
$reply_form->succ_href = getLongTablePageRawUri($page);
};
$reply_form->ctrl_enter_submit = true;
$reply_form->runAtServer();
$comments_pag = new Paginator([
'col_names' => ['*'],
'table_name' => 'blogs_comments',
'cond' => 'blog_id = ' . $blog['id'] . ' and reply_id = 0',
'tail' => 'order by id asc',
'page_len' => 20
]);
?>
<?php echoUOJPageHeader(HTML::stripTags($blog['title']) . ' - 博客') ?> <?php echoUOJPageHeader(HTML::stripTags($blog['title']) . ' - 博客') ?>
<?php echoBlog($blog, array('show_title_only' => isset($_GET['page']) && $_GET['page'] != 1)) ?> <?php UOJBlog::cur()->echoView(['show_title_only' => isset($_GET['page']) && $_GET['page'] != 1]) ?>
<h2> <h2>
评论 评论
<i class="bi bi-chat-fill"></i> <i class="bi bi-chat-fill"></i>
</h2> </h2>
<div class="list-group"> <div class="list-group">
<?php if ($comments_pag->isEmpty()): ?> <?php if ($comments_pag->isEmpty()) : ?>
<div class="list-group-item text-muted">暂无评论</div> <div class="list-group-item text-muted">暂无评论</div>
<?php else: ?> <?php else : ?>
<?php foreach ($comments_pag->get() as $comment): <?php foreach ($comments_pag->get() as $comment) :
$poster = queryUser($comment['poster']); $poster = UOJUser::query($comment['poster']);
$esc_email = HTML::escape($poster['email']); $esc_email = HTML::escape($poster['email']);
$asrc = HTML::avatar_addr($poster, 80); $asrc = HTML::avatar_addr($poster, 80);
$replies = DB::selectAll("select id, poster, content, post_time from blogs_comments where reply_id = {$comment['id']} order by id"); $replies = DB::selectAll([
foreach ($replies as $idx => $reply) { "select id, poster, content, post_time, is_hidden, reason_to_hide from blogs_comments",
$replies[$idx]['poster_realname'] = queryUser($reply['poster'])['realname']; "where", ["reply_id" => $comment['id']],
} "order by id"
$replies_json = json_encode($replies); ]);
foreach ($replies as $idx => $reply) {
$replies[$idx]['poster_realname'] = UOJUser::query($reply['poster'])['realname'];
$replies[$idx]['content'] = getCommentContentToDisplay($reply);
}
$replies_json = json_encode($replies);
?> ?>
<div id="comment-<?= $comment['id'] ?>" class="list-group-item"> <div id="comment-<?= $comment['id'] ?>" class="list-group-item">
<div class="d-flex"> <div class="d-flex">
<div class="comtposterbox mr-3 flex-shrink-0"> <div class="comtposterbox mr-3 flex-shrink-0">
<a href="<?= HTML::url('/user/'.$poster['username']) ?>" class="d-none d-sm-block text-decoration-none"> <a href="<?= HTML::url('/user/' . $poster['username']) ?>" class="d-none d-sm-block text-decoration-none">
<img class="media-object img-rounded" src="<?= $asrc ?>" alt="avatar" /> <img class="media-object img-rounded" src="<?= $asrc ?>" alt="avatar" />
</a>
</div>
<div id="comment-body-<?= $comment['id'] ?>" class="comtbox flex-grow-1 ms-3">
<div class="row">
<div class="col-sm-6"><?= getUserLink($poster['username']) ?></div>
<div class="col-sm-6 text-end"><?= getClickZanBlock('BC', $comment['id'], $comment['zan']) ?></div>
</div>
<div class="comtbox1"><?= $comment['content'] ?></div>
<ul class="list-inline mb-0 text-end">
<li>
<small class="text-muted">
<?= $comment['post_time'] ?>
</small>
</li>
<li>
<a class="text-decoration-none" id="reply-to-<?= $comment['id'] ?>" href="#">
回复
</a> </a>
</li> </div>
</ul> <div id="comment-body-<?= $comment['id'] ?>" class="comtbox flex-grow-1 ms-3">
<?php if ($replies): ?> <div class="row">
<div id="replies-<?= $comment['id'] ?>" class="comtbox5"></div> <div class="col-sm-6"><?= getUserLink($poster['username']) ?></div>
<?php endif ?> <div class="col-sm-6 text-end"><?= ClickZans::getBlock('BC', $comment['id'], $comment['zan']) ?></div>
<script type="text/javascript">showCommentReplies('<?= $comment['id'] ?>', <?= $replies_json ?>);</script> </div>
<div class="comtbox1"><?= $comment['content'] ?></div>
<ul class="list-inline mb-0 text-end">
<li>
<small class="text-muted">
<?= $comment['post_time'] ?>
</small>
</li>
<li>
<a class="text-decoration-none" id="reply-to-<?= $comment['id'] ?>" href="#">
回复
</a>
</li>
</ul>
<?php if ($replies) : ?>
<div id="replies-<?= $comment['id'] ?>" class="comtbox5"></div>
<?php endif ?>
<script type="text/javascript">
showCommentReplies('<?= $comment['id'] ?>', <?= $replies_json ?>);
</script>
</div>
</div>
</div> </div>
</div> <?php endforeach ?>
</div> <?php endif ?>
<?php endforeach ?>
<?php endif ?>
</div> </div>
<?= $comments_pag->pagination() ?> <?= $comments_pag->pagination() ?>

View File

@ -1,49 +1,29 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requirePHPLib('form'); requirePHPLib('form');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin();
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { UOJBlog::init(UOJRequest::get('id')) || UOJResponse::page404();
become403Page(); UOJBlog::cur()->belongsToUserBlog() || UOJResponse::page404();
} UOJBlog::cur()->userCanManage(Auth::user()) || UOJResponse::page403();
if (!UOJContext::hasBlogPermission()) { $delete_form = new UOJBs4Form('delete');
become403Page(); $delete_form->handle = function () {
} UOJBlog::cur()->delete();
if (!isset($_GET['id']) || !validateUInt($_GET['id']) || !($blog = queryBlog($_GET['id'])) || !UOJContext::isHis($blog)) { };
become404Page(); $delete_form->submit_button_config['class_str'] = 'btn btn-danger';
} $delete_form->submit_button_config['text'] = '是的,我确定要删除';
$delete_form->succ_href = HTML::blog_url(UOJBlog::info('poster'), '/archive');
$delete_form = new UOJForm('delete');
$delete_form->handle = function() {
global $myUser, $blog;
if ($myUser['username'] != $blog['poster']) { $delete_form->runAtServer();
$poster_user_link = getUserLink($blog['poster']); ?>
$admin_user_link = isSuperUser($myUser) ? '管理员' : getUserLink($myUser['username']); <?php echoUOJPageHeader('删除博客 - ' . HTML::stripTags(UOJBlog::info('title'))) ?>
$blog_content = HTML::escape($blog['content_md']);
$content = <<<EOD
<p>{$poster_user_link} 您好:</p>
<p>您的博客 <b>{$blog['title']}</b>ID{$blog['id']})已经被 {$admin_user_link} 删除,现将博客原文备份发送给您,请查收。</p>
<pre><code class="language-markdown">{$blog_content}</code></pre>
EOD;
sendSystemMsg($blog['poster'], '博客删除通知', $content);
}
deleteBlog($blog['id']); <h1 class="h2 text-center">
}; 您真的要删除博客 <?= UOJBlog::info('title') ?>” <span class="fs-5">(博客 ID<?= UOJBlog::info('id') ?></span>吗?该操作不可逆!
$delete_form->submit_button_config['class_str'] = 'btn btn-danger';
$delete_form->submit_button_config['text'] = '是的,我确定要删除';
$delete_form->succ_href = HTML::blog_url($blog['poster'], '/archive');
$delete_form->runAtServer();
?>
<?php echoUOJPageHeader('删除博客 - ' . HTML::stripTags($blog['title'])) ?>
<h1 class="h3 text-center">
您真的要删除博客 <?= $blog['title'] ?>” <span class="fs-5">(博客 ID<?= $blog['id'] ?></span>吗?该操作不可逆!
</h1> </h1>
<?php $delete_form->printHTML(); ?> <?php $delete_form->printHTML(); ?>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,113 +1,119 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requirePHPLib('form'); requirePHPLib('form');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { UOJUserBlog::userCanManage(Auth::user()) || UOJResponse::page403();
redirectToLogin();
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { if (!Auth::check()) {
become403Page(); redirectToLogin();
} }
if (!UOJContext::hasBlogPermission()) { if (isset($_GET['id'])) {
become403Page(); UOJBlog::init(UOJRequest::get('id')) || UOJResponse::page404();
} UOJBlog::cur()->belongsToUserBlog() || UOJResponse::page404();
if (isset($_GET['id'])) { UOJBlog::info('type') == 'B' || UOJResponse::page404();
if (!validateUInt($_GET['id']) || !($blog = queryBlog($_GET['id'])) || !UOJContext::isHisBlog($blog)) { $blog = UOJBlog::info();
become404Page(); $blog['content'] = UOJBlog::cur()->queryContent()['content'];
} $blog['content_md'] = UOJBlog::cur()->queryContent()['content_md'];
} }
$blog_editor = new UOJBlogEditor(); $blog_editor = new UOJBlogEditor();
$blog_editor->name = 'blog'; $blog_editor->name = 'blog';
if ($blog) {
$blog_editor->cur_data = array(
'title' => $blog['title'],
'content_md' => $blog['content_md'],
'content' => $blog['content'],
'tags' => UOJBlog::cur()->queryTags(),
'is_hidden' => $blog['is_hidden']
);
} else {
$blog_editor->cur_data = array(
'title' => $_GET['title'] ?: '新博客',
'content_md' => '',
'content' => '',
'tags' => [],
'is_hidden' => isset($_GET['is_hidden']) ? $_GET['is_hidden'] : true,
);
}
if ($blog) {
$blog_editor->blog_url = HTML::blog_url(UOJUserBlog::id(), "/post/{$blog['id']}");
} else {
$blog_editor->blog_url = null;
}
function updateBlog($id, $data) {
DB::update([
"update blogs",
"set", [
"title" => $data['title'],
"content" => $data['content'],
"content_md" => $data['content_md'],
"is_hidden" => $data['is_hidden']
],
"where", ["id" => $id]
]);
}
function insertBlog($data) {
DB::insert([
"insert into blogs",
"(title, content, content_md, poster, is_hidden, post_time, active_time)",
"values", DB::tuple([
$data['title'], $data['content'], $data['content_md'],
UOJUserBlog::id(), $data['is_hidden'], DB::now(), DB::now()
])
]);
}
$blog_editor->save = function ($data) {
global $blog;
$ret = [];
if ($blog) { if ($blog) {
$blog_editor->cur_data = array( updateBlog($blog['id'], $data);
'title' => $blog['title'],
'content_md' => $blog['content_md'],
'content' => $blog['content'],
'tags' => queryBlogTags($blog['id']),
'is_hidden' => $blog['is_hidden']
);
} else { } else {
$blog_editor->cur_data = array( insertBlog($data);
'title' => $_GET['title'] ?: '新博客', $blog_id = DB::insert_id();
'content_md' => '', (new UOJBlog(['id' => $blog_id, 'type' => 'B']))->setAsCur();
'content' => '', $ret['blog_id'] = $blog_id;
'tags' => array(), $ret['blog_write_url'] = UOJBlog::cur()->getUriForWrite();
'is_hidden' => isset($_GET['is_hidden']) ? $_GET['is_hidden'] : true, $ret['blog_url'] = UOJBlog::cur()->getBlogUri();
);
} }
if ($blog) { UOJBlog::cur()->updateTags($data['tags']);
$blog_editor->blog_url = HTML::blog_url(UOJContext::userid(), "/post/{$blog['id']}"); return $ret;
} else { };
$blog_editor->blog_url = null;
} $blog_editor->runAtServer();
?>
function updateBlog($id, $data) {
DB::update("update blogs set title = '".DB::escape($data['title'])."', content = '".DB::escape($data['content'])."', content_md = '".DB::escape($data['content_md'])."', is_hidden = {$data['is_hidden']} where id = {$id}");
}
function insertBlog($data) {
DB::insert("insert into blogs (title, content, content_md, poster, is_hidden, post_time) values ('".DB::escape($data['title'])."', '".DB::escape($data['content'])."', '".DB::escape($data['content_md'])."', '".UOJContext::userid()."', {$data['is_hidden']}, now())");
}
$blog_editor->save = function($data) {
global $blog;
$ret = array();
if ($blog) {
updateBlog($blog['id'], $data);
} else {
insertBlog($data);
$blog = array('id' => DB::insert_id(), 'tags' => array());
$ret['blog_id'] = $blog['id'];
$ret['blog_write_url'] = HTML::blog_url(UOJContext::userid(), "/post/{$blog['id']}/write");
$ret['blog_url'] = HTML::blog_url(UOJContext::userid(), "/post/{$blog['id']}");
}
if ($data['tags'] !== $blog['tags']) {
DB::delete("delete from blogs_tags where blog_id = {$blog['id']}");
foreach ($data['tags'] as $tag) {
DB::insert("insert into blogs_tags (blog_id, tag) values ({$blog['id']}, '".DB::escape($tag)."')");
}
}
return $ret;
};
$blog_editor->runAtServer();
?>
<?php echoUOJPageHeader('写博客') ?> <?php echoUOJPageHeader('写博客') ?>
<div class="card"> <div class="card">
<div class="card-header bg-transparent d-flex justify-content-between"> <div class="card-header bg-transparent d-flex justify-content-between">
<div class="fw-bold">写博客</div> <div class="fw-bold">写博客</div>
<div id="div-blog-id" <div id="div-blog-id" <?php if (!$blog) : ?> style="display: none" <?php endif ?>>
<?php if (!$blog): ?> <?php if ($blog) : ?>
style="display: none" <small>博客 ID<b><?= $blog['id'] ?></b></small>
<?php endif ?> <?php endif ?>
> </div>
<?php if ($blog): ?> </div>
<small>博客 ID<b><?= $blog['id'] ?></b></small> <div class="card-body">
<?php endif ?> <?php $blog_editor->printHTML() ?>
</div> </div>
</div>
<div class="card-body">
<?php $blog_editor->printHTML() ?>
</div>
</div> </div>
<!-- 提示信息 --> <!-- 提示信息 -->
<div class="card mt-3"> <div class="card mt-3">
<div class="card-body"> <div class="card-body">
<h2 class="h4 card-title">提示</h2> <h2 class="h3 card-title">提示</h2>
<ol> <ol>
<li>题解发布后还需要返回对应题目的题解页面 <b>手动输入博客 ID</b> 来将本文添加到题目的题解列表中(博客 ID 可以在右上角找到);</li> <li>题解发布后还需要返回对应题目的题解页面 <b>手动输入博客 ID</b> 来将本文添加到题目的题解列表中(博客 ID 可以在右上角找到);</li>
<li>请勿引用不稳定的外部资源(如来自个人服务器的图片或文档等),以便备份及后期维护;</li> <li>请勿引用不稳定的外部资源(如来自个人服务器的图片或文档等),以便备份及后期维护;</li>
<li>请勿在博文中直接插入大段 HTML 代码,这可能会破坏页面的显示,可以考虑使用 <a class="text-decoration-none" href="/html2markdown" target="_blank">转换工具</a> 转换后再作修正;</li> <li>请勿在博文中直接插入大段 HTML 代码,这可能会破坏页面的显示,可以考虑使用 <a class="text-decoration-none" href="/html2markdown" target="_blank">转换工具</a> 转换后再作修正;</li>
<li>图片上传推荐使用 <a class="text-decoration-none" href="/image_hosting" target="_blank">S2OJ 图床</a>,以免后续产生外链图片大量失效的情况。</li> <li>图片上传推荐使用 <a class="text-decoration-none" href="/image_hosting" target="_blank">S2OJ 图床</a>,以免后续产生外链图片大量失效的情况。</li>
</ol> </ol>
<p class="card-text"> <p class="card-text">
帮助:<a class="text-decoration-none" href="http://uoj.ac/blog/7">UOJ 博客使用教程</a> 帮助:<a class="text-decoration-none" href="http://uoj.ac/blog/7">UOJ 博客使用教程</a>
</p> </p>
</div> </div>
</div> </div>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,67 +1,63 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requireLib('hljs'); requireLib('hljs');
requireLib('mathjax'); requireLib('mathjax');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin();
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { $blogs_pag = new Paginator([
become403Page(); 'col_names' => ['*'],
} 'table_name' => 'blogs',
'cond' => "poster = '" . UOJUserBlog::id() . "' and is_hidden = 0",
'tail' => 'order by post_time desc',
'page_len' => 5
]);
$blogs_pag = new Paginator(array( $all_tags = DB::selectAll("select distinct tag from blogs_tags where blog_id in (select id from blogs where $blogs_cond)");
'col_names' => array('*'), ?>
'table_name' => 'blogs', <?php echoUOJPageHeader(UOJUserBlog::id() . '的博客') ?>
'cond' => "poster = '".UOJContext::userid()."' and is_hidden = 0",
'tail' => 'order by post_time desc',
'page_len' => 5
));
$all_tags = DB::selectAll("select distinct tag from blogs_tags where blog_id in (select id from blogs where $blogs_cond)");
?>
<?php echoUOJPageHeader(UOJContext::user()['username'] . '的博客') ?>
<div class="row"> <div class="row">
<div class="col-lg-9"> <div class="col-lg-9">
<?php if ($blogs_pag->isEmpty()): ?> <?php if ($blogs_pag->isEmpty()) : ?>
<div class="text-muted">此人很懒,什么博客也没留下。</div> <div class="text-muted">此人很懒,什么博客也没留下。</div>
<?php else: ?> <?php else : ?>
<?php foreach ($blogs_pag->get() as $blog): ?> <?php
<?php echoBlog($blog, array('is_preview' => true)) ?> foreach ($blogs_pag->get() as $blog_info) {
<?php endforeach ?> $blog = new UOJBlog($blog_info);
$blog->echoView(['is_preview' => true]);
}
?>
<?php endif ?> <?php endif ?>
<div class="text-center"> <?= $blogs_pag->pagination() ?>
<?= $blogs_pag->pagination(); ?>
</div>
</div> </div>
<div class="col-lg-3"> <div class="col-lg-3">
<img class="media-object img-thumbnail center-block" alt="<?= UOJContext::user()['username'] ?> Avatar" src="<?= HTML::avatar_addr(UOJContext::user(), 512) ?>" /> <img class="media-object img-thumbnail center-block" alt="<?= UOJUserBlog::id() ?> Avatar" src="<?= HTML::avatar_addr(UOJUserBlog::user(), 512) ?>" />
<?php if (UOJContext::hasBlogPermission()): ?> <?php if (UOJUserBlog::userCanManage(Auth::user())) : ?>
<div class="btn-group d-flex mt-3"> <div class="btn-group d-flex mt-3">
<a href="<?= HTML::blog_url(UOJContext::userid(), '/post/new/write') ?>" class="btn btn-primary"> <a href="<?= HTML::blog_url(UOJUserBlog::id(), '/post/new/write') ?>" class="btn btn-primary">
<i class="bi bi-pencil-square"></i> <i class="bi bi-pencil-square"></i>
写新博客 写新博客
</a> </a>
<a href="<?= HTML::blog_url(UOJContext::userid(), '/slide/new/write') ?>" class="btn btn-primary"> <a href="<?= HTML::blog_url(UOJUserBlog::id(), '/slide/new/write') ?>" class="btn btn-primary">
<i class="bi bi-file-earmark-slides"></i> <i class="bi bi-file-earmark-slides"></i>
写新幻灯片 写新幻灯片
</a> </a>
</div> </div>
<?php endif ?> <?php endif ?>
<div class="card border-info mt-3"> <div class="card border-info mt-3">
<div class="card-header bg-info">标签</div> <div class="card-header bg-info">标签</div>
<div class="card-body"> <div class="card-body">
<?php if ($all_tags): ?> <?php if ($all_tags) : ?>
<?php foreach ($all_tags as $tag): ?> <?php foreach ($all_tags as $tag) : ?>
<?php echoBlogTag($tag['tag']) ?> <?php echoBlogTag($tag['tag']) ?>
<?php endforeach ?> <?php endforeach ?>
<?php else: ?> <?php else : ?>
<div class="text-muted">暂无</div> <div class="text-muted">暂无</div>
<?php endif ?> <?php endif ?>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,12 +1,12 @@
<?php <?php
call_user_func(function() { // to prevent variable scope leak call_user_func(function () { // to prevent variable scope leak
Route::pattern('id', '[1-9][0-9]{0,9}'); Route::pattern('id', '[1-9][0-9]{0,9}');
Route::pattern('blog_username', '[a-zA-Z0-9_\-]{1,20}'); Route::pattern('blog_username', '[a-zA-Z0-9_\-]{1,20}');
switch (UOJConfig::$data['switch']['blog-domain-mode']) { switch (UOJConfig::$data['switch']['blog-domain-mode']) {
case 1: case 1:
$domain = '{blog_username}.'.UOJConfig::$data['web']['blog']['host']; $domain = '{blog_username}.' . UOJConfig::$data['web']['blog']['host'];
$prefix = ''; $prefix = '';
break; break;
case 2: case 2:
@ -19,20 +19,24 @@ call_user_func(function() { // to prevent variable scope leak
break; break;
} }
Route::group([ Route::group(
[
'domain' => UOJConfig::$data['web']['blog']['host'] 'domain' => UOJConfig::$data['web']['blog']['host']
], function() { ],
function () {
Route::any("/", '/blogs.php'); Route::any("/", '/blogs.php');
Route::any("/blogs/{id}", '/blog_show.php'); Route::any("/blogs/{id}", '/blog_show.php');
Route::any("/post/{id}", '/blog_show.php'); Route::any("/post/{id}", '/blog_show.php');
} }
); );
Route::group([ Route::group(
[
'domain' => $domain, 'domain' => $domain,
'onload' => function() { 'onload' => function () {
UOJContext::setupBlog(); UOJContext::setupBlog();
} }
], function() use ($prefix) { ],
function () use ($prefix) {
Route::any("$prefix/", '/subdomain/blog/index.php'); Route::any("$prefix/", '/subdomain/blog/index.php');
Route::any("$prefix/archive", '/subdomain/blog/archive.php'); Route::any("$prefix/archive", '/subdomain/blog/archive.php');
Route::any("$prefix/aboutme", '/subdomain/blog/aboutme.php'); Route::any("$prefix/aboutme", '/subdomain/blog/aboutme.php');

View File

@ -1,84 +1,86 @@
<?php <?php
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { requireLib('bootstrap5');
redirectToLogin(); requireLib('mathjax');
} requirePHPLib('form');
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
become403Page(); ?>
}
requireLib('bootstrap5');
requireLib('mathjax');
requirePHPLib('form');
$username = UOJContext::userid();
?>
<?php echoUOJPageHeader(UOJLocale::get('contests::contest self reviews')) ?> <?php echoUOJPageHeader(UOJLocale::get('contests::contest self reviews')) ?>
<h1 class="h2"> <h1>
<?= $username ?> 的所有赛后总结 <?= UOJUserBlog::id() ?> 的所有赛后总结
</h1> </h1>
<?php <?php
$col_names = array('contest_id'); $col_names = ['contest_id'];
$from = 'contests_registrants a left join contests b on a.contest_id = b.id'; $from = 'contests_registrants inner join contests on contests_registrants.contest_id = contests.id';
$cond = "username = '$username' and has_participated = 1"; $cond = ["username" => UOJUserBlog::id(), "has_participated" => 1];
$tail = 'order by start_time desc, id desc'; $tail = 'order by start_time desc, id desc';
$config = array( $config = [
'pagination_table' => 'contests_registrants', 'page_len' => 10,
'page_len' => 10, 'div_classes' => ['card', 'card-default', 'table-responsive'],
'div_classes' => ['card', 'card-default', 'table-responsive'], 'table_classes' => ['table', 'table-bordered', 'text-center', 'align-middle', 'uoj-table', 'mb-0'],
'table_classes' => ['table', 'table-bordered', 'text-center', 'align-middle', 'uoj-table', 'mb-0'], ];
);
$header_row = ''; $header_row = '';
$header_row .= '<tr>'; $header_row .= '<tr>';
$header_row .= '<th style="width: 28em;">'.UOJLocale::get('contests::contest name').'</th>'; $header_row .= '<th style="width:28em">' . UOJLocale::get('contests::contest name') . '</th>';
$header_row .= '<th style="width: 14em;">'.UOJLocale::get('problems::problem').'</th>'; $header_row .= '<th style="width:14em">' . UOJLocale::get('problems::problem') . '</th>';
$header_row .= '<th style="width: 35em;">'.UOJLocale::get('contests::problem self review').'</th>'; $header_row .= '<th style="width:35em">' . UOJLocale::get('contests::problem self review') . '</th>';
$header_row .= '<th style="width: 35em;">'.UOJLocale::get('contests::contest self review').'</th>'; $header_row .= '<th style="width:35em">' . UOJLocale::get('contests::contest self review') . '</th>';
$header_row .= '</tr>'; $header_row .= '</tr>';
$parsedown = HTML::parsedown(); $parsedown = HTML::parsedown();
$purifier = HTML::purifier_inline(); $purifier = HTML::purifier_inline();
$print_row = function($row) use ($parsedown, $purifier) { $print_row = function ($row) use ($parsedown, $purifier) {
global $username; $contest = UOJContest::query($row['contest_id']);
$problems = $contest->getProblemIDs();
$result = '';
$contest_id = $row['contest_id']; for ($i = 0; $i < count($problems); $i++) {
$contest = queryContest($contest_id); $problem = UOJContestProblem::query($problems[$i], $contest);
$contest_problems = queryContestProblems($contest_id); $review = DB::selectSingle([
$n_contest_problems = count($contest_problems); "select content",
"from contests_reviews",
"where", [
"contest_id" => $contest->info['id'],
"problem_id" => $problem->info['id'],
"poster" => UOJUserBlog::id(),
]
]);
for ($i = 0; $i < $n_contest_problems; $i++) { $result .= '<tr>';
$problem_id = $contest_problems[$i]['problem_id'];
$problem = queryProblemBrief($problem_id);
$problem_self_review = DB::selectFirst("select content from contests_reviews where contest_id = $contest_id and problem_id = $problem_id and poster = '$username'");
$result .= '<tr>'; if ($i == 0) {
$result .= '<td rowspan="' . count($problems) . '"><a href="' . $contest->getUri() . '">' . $contest->info['name'] . '</a></td>';
if ($i == 0) {
$result .= '<td rowspan="' . $n_contest_problems . '"><a href="' . HTML::url("/contest/$contest_id") . '">' . $contest['name'] . '</a></td>';
}
$problem_review_id = "review-$contest_id-$i";
$result .= '<td>' . chr(ord('A') + $i) . '. <a href="/problem/' . $problem_id . '">' . $problem['title'] . '</a></td>';
$result .= '<td>' . $purifier->purify($problem_self_review != null ? $parsedown->line($problem_self_review['content']) : '') . '</td>';
if ($i == 0) {
$contest_review_id = "review-$contest_id-overall";
$contest_self_review = DB::selectFirst("select content from contests_reviews where contest_id = $contest_id and problem_id = -1 and poster = '$username'");
$result .= '<td rowspan="' . $n_contest_problems . '">' . $purifier->purify($contest_self_review != null ? $parsedown->line($contest_self_review['content']) : '') . '</td>';
}
$result .= '</tr>';
} }
echo $result; $result .= '<td>' . $problem->getLink(['with' => 'letter', 'simplify' => true]) . '</td>';
}; $result .= '<td>' . $purifier->purify($review ? $parsedown->line($review) : '') . '</td>';
echoLongTable($col_names, $from, $cond, $tail, $header_row, $print_row, $config); if ($i == 0) {
?> $review = DB::selectSingle([
"select content",
"from contests_reviews",
"where", [
"contest_id" => $contest->info['id'],
"problem_id" => -1,
"poster" => UOJUserBlog::id(),
]
]);
$result .= '<td rowspan="' . count($problems) . '">' . $purifier->purify($review ? $parsedown->line($review) : '') . '</td>';
}
$result .= '</tr>';
}
echo $result;
};
echoLongTable($col_names, $from, $cond, $tail, $header_row, $print_row, $config);
?>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,23 +1,16 @@
<?php <?php
requirePHPLib('form'); requirePHPLib('form');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin(); UOJBlog::init(UOJRequest::get('id')) || UOJResponse::page404();
} UOJBlog::cur()->belongsToUserBlog() || UOJResponse::page404();
UOJBlog::cur()->userCanView(Auth::user()) || UOJResponse::page403();
UOJBlog::cur()->isTypeS() || UOJResponse::page404();
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) {
become403Page(); $page_config = UOJContext::pageConfig();
} $page_config += [
'PageTitle' => HTML::stripTags(UOJBlog::info('title')) . ' - 幻灯片',
if (!isset($_GET['id']) || !validateUInt($_GET['id']) || !($blog = queryBlog($_GET['id'])) || !UOJContext::isHisSlide($blog)) { 'content' => UOJBlog::cur()->queryContent()['content']
become404Page(); ];
} uojIncludeView('slide', $page_config);
if ($blog['is_hidden'] && !UOJContext::hasBlogPermission()) {
become403Page();
}
$page_config = UOJContext::pageConfig();
$page_config['PageTitle'] = HTML::stripTags($blog['title']) . ' - 幻灯片';
$page_config['content'] = $blog['content'];
uojIncludeView('slide', $page_config);
?>

View File

@ -1,82 +1,95 @@
<?php <?php
requirePHPLib('form'); requireLib('bootstrap5');
requireLib('bootstrap5'); requirePHPLib('form');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin(); UOJUserBlog::userCanManage(Auth::user()) || UOJResponse::page403();
}
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { if (isset($_GET['id'])) {
become403Page(); UOJBlog::init(UOJRequest::get('id')) || UOJResponse::page404();
} UOJBlog::cur()->belongsToUserBlog() || UOJResponse::page404();
UOJBlog::info('type') == 'S' || UOJResponse::page404();
if (!UOJContext::hasBlogPermission()) { $blog = UOJBlog::info();
become403Page(); $blog['content'] = UOJBlog::cur()->queryContent()['content'];
} $blog['content_md'] = UOJBlog::cur()->queryContent()['content_md'];
if (isset($_GET['id'])) { }
if (!validateUInt($_GET['id']) || !($blog = queryBlog($_GET['id'])) || !UOJContext::isHisSlide($blog)) {
become404Page(); $blog_editor = new UOJBlogEditor();
} $blog_editor->type = 'slide';
} $blog_editor->name = 'blog';
if ($blog) {
$blog_editor = new UOJBlogEditor(); $blog_editor->cur_data = array(
$blog_editor->type = 'slide'; 'title' => $blog['title'],
$blog_editor->name = 'blog'; 'content_md' => $blog['content_md'],
'content' => $blog['content'],
'tags' => UOJBlog::cur()->queryTags(),
'is_hidden' => $blog['is_hidden']
);
} else {
$blog_editor->cur_data = array(
'title' => '新幻灯片',
'content_md' => '',
'content' => '',
'tags' => [],
'is_hidden' => true
);
}
if ($blog) {
$blog_editor->blog_url = HTML::blog_url(UOJUserBlog::id(), "/post/{$blog['id']}");
} else {
$blog_editor->blog_url = null;
}
function insertBlog($data) {
DB::insert([
"insert into blogs",
"(title, content, content_md, poster, is_hidden, post_time, active_time)",
"values", DB::tuple([
$data['title'], $data['content'], $data['content_md'],
UOJUserBlog::id(), $data['is_hidden'], DB::now(), DB::now()
])
]);
}
function insertSlide($data) {
DB::insert("insert into blogs (type, title, content, content_md, poster, is_hidden, post_time) values ('S', '" . DB::escape($data['title']) . "', '" . DB::escape($data['content']) . "', '" . DB::escape($data['content_md']) . "', '" . Auth::id() . "', {$data['is_hidden']}, now())");
}
$blog_editor->save = function ($data) {
global $blog;
$ret = [];
if ($blog) { if ($blog) {
$blog_editor->cur_data = array( updateBlog($blog['id'], $data);
'title' => $blog['title'],
'content_md' => $blog['content_md'],
'content' => $blog['content'],
'tags' => queryBlogTags($blog['id']),
'is_hidden' => $blog['is_hidden']
);
} else { } else {
$blog_editor->cur_data = array( insertSlide($data);
'title' => '新幻灯片', $blog_id = DB::insert_id();
'content_md' => '', (new UOJBlog(['id' => $blog_id, 'type' => 'S']))->setAsCur();
'content' => '', $ret['blog_id'] = $blog_id;
'tags' => array(), $ret['blog_write_url'] = UOJBlog::cur()->getUriForWrite();
'is_hidden' => true $ret['blog_url'] = UOJBlog::cur()->getBlogUri();
);
} }
if ($blog) { UOJBlog::cur()->updateTags($data['tags']);
$blog_editor->blog_url = HTML::blog_url(UOJContext::user()['username'], "/post/{$blog['id']}"); return $ret;
} else { };
$blog_editor->blog_url = null;
} $blog_editor->runAtServer();
?>
function updateBlog($id, $data) {
DB::update("update blogs set title = '".DB::escape($data['title'])."', content = '".DB::escape($data['content'])."', content_md = '".DB::escape($data['content_md'])."', is_hidden = {$data['is_hidden']} where id = {$id}");
}
function insertSlide($data) {
DB::insert("insert into blogs (type, title, content, content_md, poster, is_hidden, post_time) values ('S', '".DB::escape($data['title'])."', '".DB::escape($data['content'])."', '".DB::escape($data['content_md'])."', '".Auth::id()."', {$data['is_hidden']}, now())");
}
$blog_editor->save = function($data) {
global $blog;
$ret = array();
if ($blog) {
updateBlog($blog['id'], $data);
} else {
insertSlide($data);
$blog = array('id' => DB::insert_id(), 'tags' => array());
$ret['blog_write_url'] = HTML::blog_url(UOJContext::user()['username'], "/slide/{$blog['id']}/write");
$ret['blog_url'] = HTML::blog_url(UOJContext::user()['username'], "/slide/{$blog['id']}");
}
if ($data['tags'] !== $blog['tags']) {
DB::delete("delete from blogs_tags where blog_id = {$blog['id']}");
foreach ($data['tags'] as $tag) {
DB::insert("insert into blogs_tags (blog_id, tag) values ({$blog['id']}, '".DB::escape($tag)."')");
}
}
return $ret;
};
$blog_editor->runAtServer();
?>
<?php echoUOJPageHeader('写幻灯片') ?> <?php echoUOJPageHeader('写幻灯片') ?>
<div class="text-end"> <div class="text-end">
<a class="text-decoration-none" href="http://uoj.ac/blog/75">这玩意儿怎么用?</a> <a class="text-decoration-none" href="http://uoj.ac/blog/75">这玩意儿怎么用?</a>
</div> </div>
<?php $blog_editor->printHTML() ?>
<div class="card">
<div class="card-header bg-transparent d-flex justify-content-between">
<div class="fw-bold">写幻灯片</div>
<div id="div-blog-id" <?php if (!$blog) : ?> style="display: none" <?php endif ?>>
<?php if ($blog) : ?>
<small>博客 ID<b><?= $blog['id'] ?></b></small>
<?php endif ?>
</div>
</div>
<div class="card-body">
<?php $blog_editor->printHTML() ?>
</div>
</div>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,160 +1,183 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requireLib('hljs'); requireLib('hljs');
requirePHPLib('form'); requirePHPLib('form');
requirePHPLib('judger'); requirePHPLib('judger');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin(); UOJSubmission::init(UOJRequest::get('id')) || UOJResponse::page404();
} UOJSubmission::initProblemAndContest() || UOJResponse::page404();
UOJSubmission::cur()->userCanView(Auth::user(), ['ensure' => true]);
if (!validateUInt($_GET['id']) || !($submission = querySubmission($_GET['id']))) {
become404Page();
}
$submission_result = json_decode($submission['result'], true); $perm = UOJSubmission::cur()->viewerCanSeeComponents(Auth::user());
$problem = queryProblemBrief($submission['problem_id']);
$problem_extra_config = getProblemExtraConfig($problem);
if ($submission['contest_id']) {
$contest = queryContest($submission['contest_id']);
genMoreContestInfo($contest);
} else {
$contest = null;
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { $can_see_minor = false;
become403Page(); if ($perm['score']) {
} $can_see_minor = UOJSubmission::cur()->userCanSeeMinorVersions(Auth::user());
UOJSubmissionHistory::init(UOJSubmission::cur(), ['minor' => $can_see_minor]) || UOJResponse::page404();
if (isset($_GET['time'])) {
$history_time = UOJRequest::get('time', 'is_short_string');
!empty($history_time) || UOJResponse::page404();
UOJSubmission::cur()->loadHistoryByTime($history_time) || UOJResponse::page404();
UOJSubmission::cur()->isMajor() || UOJResponse::page404();
} elseif (isset($_GET['tid'])) {
$can_see_minor || UOJResponse::page404();
UOJSubmission::cur()->loadHistoryByTID(UOJRequest::get('tid', 'validateUInt')) || UOJResponse::page404();
!UOJSubmission::cur()->isMajor() || UOJResponse::page404();
} }
}
if (!isSubmissionVisibleToUser($submission, $problem, $myUser)) { $submission = UOJSubmission::info();
become403Page(); $submission_result = UOJSubmission::cur()->getResult();
} $problem = UOJProblem::info();
if ($can_see_minor) {
$minor_rejudge_form = new UOJBs4Form('minor_rejudge');
$minor_rejudge_form->handle = function () {
UOJSubmission::rejudgeById(UOJSubmission::info('id'), [
'reason_text' => '管理员偷偷重测该提交记录',
'major' => false
]);
$tid = DB::insert_id();
redirectTo(UOJSubmission::cur()->getUriForNewTID($tid));
};
$minor_rejudge_form->submit_button_config['class_str'] = 'btn btn-sm btn-primary';
$minor_rejudge_form->submit_button_config['text'] = '偷偷重新测试';
$minor_rejudge_form->submit_button_config['align'] = 'right';
$minor_rejudge_form->runAtServer();
}
if (UOJSubmission::cur()->isLatest()) {
if (UOJSubmission::cur()->preHackCheck()) {
$hack_form = new UOJBs4Form('hack');
$out_status = explode(', ', $submission['status'])[0];
if ($_GET['get'] == 'status-details' && Auth::check() && $submission['submitter'] === Auth::id()) {
echo json_encode(array(
'judged' => $out_status == 'Judged',
'html' => getSubmissionStatusDetails($submission)
));
die();
}
$hackable = $submission['score'] == 100 && $problem['hackable'] == 1;
if ($hackable) {
$hack_form = new UOJForm('hack');
$hack_form->addTextFileInput('input', '输入数据'); $hack_form->addTextFileInput('input', '输入数据');
$hack_form->addCheckBox('use_formatter', '帮我整理文末回车、行末空格、换行符', true); $hack_form->addCheckBox('use_formatter', '帮我整理文末回车、行末空格、换行符', true);
$hack_form->handle = function(&$vdata) { $hack_form->handle = function (&$vdata) {
global $myUser, $problem, $submission; global $problem, $submission;
if ($myUser == null) { Auth::check() || redirectToLogin();
redirectToLogin();
}
if ($_POST["input_upload_type"] == 'file') { if ($_POST["input_upload_type"] == 'file') {
$tmp_name = UOJForm::uploadedFileTmpName("input_file"); $tmp_name = UOJBs4Form::uploadedFileTmpName("input_file");
if ($tmp_name == null) { if ($tmp_name == null) {
becomeMsgPage('你在干啥……怎么什么都没交过来……?'); UOJResponse::message('你在干啥……怎么什么都没交过来……?');
} }
} }
$fileName = uojRandAvaiableTmpFileName(); $fileName = FS::randomAvailableTmpFileName();
$fileFullName = UOJContext::storagePath().$fileName; $fileFullName = UOJContext::storagePath() . $fileName;
if ($_POST["input_upload_type"] == 'editor') { if ($_POST["input_upload_type"] == 'editor') {
file_put_contents($fileFullName, $_POST['input_editor']); file_put_contents($fileFullName, $_POST['input_editor']);
} else { } else {
move_uploaded_file($_FILES["input_file"]['tmp_name'], $fileFullName); move_uploaded_file($_FILES["input_file"]['tmp_name'], $fileFullName);
} }
$input_type = isset($_POST['use_formatter']) ? "USE_FORMATTER" : "DONT_USE_FORMATTER"; $input_type = isset($_POST['use_formatter']) ? "USE_FORMATTER" : "DONT_USE_FORMATTER";
DB::insert("insert into hacks (problem_id, submission_id, hacker, owner, input, input_type, submit_time, details, is_hidden) values ({$problem['id']}, {$submission['id']}, '{$myUser['username']}', '{$submission['submitter']}', '$fileName', '$input_type', now(), '', {$problem['is_hidden']})"); DB::insert([
"insert into hacks",
"(problem_id, submission_id, hacker, owner, input, input_type, submit_time, status, details, is_hidden)",
"values", DB::tuple([
$problem['id'], $submission['id'], Auth::id(), $submission['submitter'],
$fileName, $input_type, DB::now(), 'Waiting', '', $problem['is_hidden']
])
]);
}; };
$hack_form->max_post_size = 25 * 1024 * 1024;
$hack_form->max_file_size_mb = 20;
$hack_form->succ_href = "/hacks"; $hack_form->succ_href = "/hacks";
$hack_form->runAtServer(); $hack_form->runAtServer();
} }
if ($submission['status'] == 'Judged' && hasProblemPermission($myUser, $problem)) { if (UOJSubmission::cur()->userCanRejudge(Auth::user())) {
$rejudge_form = new UOJForm('rejudge'); $rejudge_form = new UOJBs4Form('rejudge');
$rejudge_form->handle = function() { $rejudge_form->handle = function () {
global $submission; UOJSubmission::rejudgeById(UOJSubmission::info('id'));
rejudgeSubmission($submission);
}; };
$rejudge_form->submit_button_config['class_str'] = 'btn btn-primary'; $rejudge_form->submit_button_config['class_str'] = 'btn btn-sm btn-primary';
$rejudge_form->submit_button_config['text'] = '重新测试'; $rejudge_form->submit_button_config['text'] = '重新测试';
$rejudge_form->submit_button_config['align'] = 'right'; $rejudge_form->submit_button_config['align'] = 'end';
$rejudge_form->runAtServer(); $rejudge_form->runAtServer();
} }
if (isSuperUser($myUser)) { if (UOJSubmission::cur()->userCanDelete(Auth::user())) {
$delete_form = new UOJForm('delete'); $delete_form = new UOJBs4Form('delete');
$delete_form->handle = function() { $delete_form->handle = function () {
global $submission; UOJSubmission::cur()->delete();
$content = json_decode($submission['content'], true);
unlink(UOJContext::storagePath().$content['file_name']);
DB::delete("delete from submissions where id = {$submission['id']}");
updateBestACSubmissions($submission['submitter'], $submission['problem_id']);
}; };
$delete_form->submit_button_config['class_str'] = 'btn btn-danger'; $delete_form->submit_button_config['class_str'] = 'btn btn-sm btn-danger';
$delete_form->submit_button_config['text'] = '删除此提交记录'; $delete_form->submit_button_config['text'] = '删除此提交记录';
$delete_form->submit_button_config['align'] = 'right'; $delete_form->submit_button_config['align'] = 'end';
$delete_form->submit_button_config['smart_confirm'] = ''; $delete_form->submit_button_config['smart_confirm'] = '';
$delete_form->succ_href = "/submissions"; $delete_form->succ_href = "/submissions";
$delete_form->runAtServer(); $delete_form->runAtServer();
} }
} else {
$should_show_content = hasViewPermission($problem_extra_config['view_content_type'], $myUser, $problem, $submission); if (UOJSubmission::cur()->userCanDelete(Auth::user()) && !UOJSubmission::cur()->isMajor()) {
$should_show_all_details = hasViewPermission($problem_extra_config['view_all_details_type'], $myUser, $problem, $submission); $delete_form = new UOJBs4Form('delete');
$should_show_details = hasViewPermission($problem_extra_config['view_details_type'], $myUser, $problem, $submission); $delete_form->handle = function () {
$should_show_details_to_me = isSuperUser($myUser); UOJSubmission::cur()->deleteThisMinorVersion();
if (explode(', ', $submission['status'])[0] != 'Judged') { };
$should_show_all_details = false; $delete_form->submit_button_config['class_str'] = 'btn btn-sm btn-danger';
$delete_form->submit_button_config['text'] = '删除当前历史记录(保留其他历史记录)';
$delete_form->submit_button_config['align'] = 'end';
$delete_form->submit_button_config['smart_confirm'] = '';
$delete_form->succ_href = UOJSubmission::cur()->getUriForLatest();
$delete_form->runAtServer();
} }
if ($contest != null && $contest['cur_progress'] == CONTEST_IN_PROGRESS) { }
if ($contest['extra_config']["problem_{$submission['problem_id']}"] === 'no-details') { ?>
$should_show_details = false;
}
}
if (!isSubmissionFullVisibleToUser($submission, $contest, $problem, $myUser)) {
$should_show_content = $should_show_all_details = false;
}
if ($contest != null && hasContestPermission($myUser, $contest)) {
$should_show_details_to_me = true;
$should_show_content = true;
$should_show_all_details = true;
}
if ($should_show_all_details) {
$styler = new SubmissionDetailsStyler();
if ((!$should_show_details || ($contest['extra_config']['contest_type']=='IOI' && $contest['cur_progress'] == CONTEST_IN_PROGRESS)) && !hasContestPermission($myUser, $contest)) {
$styler->fade_all_details = true;
$styler->show_small_tip = false;
if ($contest['extra_config']['contest_type']=='IOI' && $contest['cur_progress'] == CONTEST_IN_PROGRESS) {
$styler->ioi_contest_is_running = true;
}
}
}
?>
<script> <script>
var problem_id = parseInt('<?= $submission['problem_id'] ?>'); var problem_id = parseInt('<?= $submission['problem_id'] ?>');
</script> </script>
<?php echoUOJPageHeader(UOJLocale::get('problems::submission').' #'.$submission['id']) ?> <?php echoUOJPageHeader(UOJLocale::get('problems::submission') . ' #' . $submission['id']) ?>
<h1 class="h3"> <h1>
<?= UOJLocale::get('problems::submission').' #'.$submission['id'] ?> <?= UOJLocale::get('problems::submission') . ' #' . $submission['id'] ?>
</h1> </h1>
<?php echoSubmissionsListOnlyOne($submission, array('id_hidden' => ''), $myUser) ?> <?php UOJSubmission::cur()->echoStatusTable(['show_actual_score' => $perm['score'], 'id_hidden' => true], Auth::user()) ?>
<?php if ($should_show_content): ?> <?php
<?php echoSubmissionContent($submission, getProblemSubmissionRequirement($problem)) ?> if ($perm['score']) {
<?php if ($hackable): ?> HTML::echoPanel('mb-3', '测评历史', function () {
UOJSubmissionHistory::cur()->echoTimeline();
});
}
?>
<?php
if ($perm['manager_view']) {
HTML::echoPanel('mb-3', '测评机信息(管理员可见)', function () {
if (empty(UOJSubmission::info('judger'))) {
echo '暂无';
} else {
$judger = DB::selectFirst([
"select * from judger_info",
"where", [
"judger_name" => UOJSubmission::info('judger')
]
]);
if (!$judger) {
echo '测评机信息损坏';
} else {
echo '<strong>', $judger['display_name'], ': </strong>', $judger['description'];
}
}
});
}
?>
<?php if ($perm['content'] || $perm['manager_view']) : ?>
<?php UOJSubmission::cur()->echoContent() ?>
<?php if (isset($hack_form)) : ?>
<p class="text-center"> <p class="text-center">
这程序好像有点Bug我给组数据试试 <button id="button-display-hack" type="button" class="btn btn-danger btn-xs">Hack!</button> 这程序好像有点Bug我给组数据试试 <button id="button-display-hack" type="button" class="btn btn-danger btn-xs">Hack!</button>
</p> </p>
<div id="div-form-hack" style="display:none" class="bot-buffer-md"> <div id="div-form-hack" style="display:none" class="mb-3">
<p class="text-center text-danger">
Hack 功能是给大家互相查错用的。请勿故意提交错误代码,然后自己 Hack 自己、贼喊捉贼哦(故意贼喊捉贼会予以封禁处理)
</p>
<?php $hack_form->printHTML() ?> <?php $hack_form->printHTML() ?>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
@ -167,36 +190,48 @@
<?php endif ?> <?php endif ?>
<?php endif ?> <?php endif ?>
<?php if ($should_show_all_details): ?> <?php
<div class="card border-info mb-3"> if (UOJSubmission::cur()->hasJudged()) {
<div class="card-header bg-info"> if ($perm['high_level_details']) {
<h4 class="card-title"><?= UOJLocale::get('details') ?></h4> HTML::echoPanel(['card' => 'mb-3', 'body' => 'p-0'], UOJLocale::get('details'), function () use ($perm, $submission_result) {
</div> $styler = new SubmissionDetailsStyler();
if (!$perm['low_level_details']) {
<?php echoJudgementDetails($submission_result['details'], $styler, 'details') ?> $styler->fade_all_details = true;
<?php if ($should_show_details_to_me): ?> $styler->show_small_tip = false;
<?php if (isset($submission_result['final_result'])): ?> }
<hr /> echoJudgmentDetails($submission_result['details'], $styler, 'details');
<?php echoSubmissionDetails($submission_result['final_result']['details'], 'final_details') ?>
<?php endif ?> if ($perm['manager_view'] && !$perm['low_level_details']) {
<?php if ($styler->fade_all_details): ?> echo '<hr />';
<hr /> echo '<h4 class="text-info">全部详细信息(管理员可见)</h4>';
<?php echoSubmissionDetails($submission_result['details'], 'final_details') ?> echoSubmissionDetails($submission_result['details'], 'all_details');
<?php endif ?> }
<?php endif ?> });
</div> } else if ($perm['manager_view']) {
HTML::echoPanel(['card' => 'mb-3', 'body' => 'p-0'], '详细(管理员可见)', function () use ($submission_result) {
echoSubmissionDetails($submission_result['details'], 'details');
});
}
if ($perm['manager_view'] && isset($submission_result['final_result'])) {
HTML::echoPanel(['card' => 'mb-3', 'body' => 'p-0'], '终测结果预测(管理员可见)', function () use ($submission_result) {
echoSubmissionDetails($submission_result['final_result']['details'], 'final_details');
});
}
}
?>
<div class="d-flex gap-2 justify-content-end">
<?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)) : ?>
<div class="text-end">
<?php $rejudge_form->printHTML() ?> <?php $rejudge_form->printHTML() ?>
</div>
<?php endif ?> <?php endif ?>
<?php if (isset($delete_form)): ?> <?php if (isset($delete_form)) : ?>
<div class="text-end">
<?php $delete_form->printHTML() ?> <?php $delete_form->printHTML() ?>
</div>
<?php endif ?> <?php endif ?>
</div>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,37 +1,18 @@
<?php <?php
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || UOJResponse::page403();
redirectToLogin(); is_array($_GET['get']) || UOJResponse::page404();
}
if (!is_array($_GET['get'])) {
become404Page();
}
$res = []; $res = [];
foreach ($_GET['get'] as $id) { foreach ($_GET['get'] as $id) {
if (!validateUInt($id)) { ($submission = UOJSubmission::query($id)) || UOJResponse::page404();
become404Page(); $submission->setProblem() || UOJResponse::page404();
} $submission->userIsSubmitter(Auth::user()) || UOJResponse::page403();
$submission = querySubmission($id); $submission->userCanView(Auth::user(), ['ensure' => true]);
if ($submission['submitter'] !== Auth::id()) {
become403Page();
}
if ($submission['contest_id'] == null && !(isNormalUser($myUser) && UOJConfig::$data['switch']['force-login'])) {
become403Page();
}
$problem = queryProblemBrief($submission['problem_id']);
if (!isSubmissionVisibleToUser($submission, $problem, Auth::user())) {
become403Page();
}
$out_status = explode(', ', $submission['status'])[0];
$res[] = [ $res[] = [
'judged' => $out_status == 'Judged', 'judged' => $submission->hasJudged(),
'html' => getSubmissionStatusDetails($submission) 'waiting' => $submission->isWaiting(),
'html' => $submission->getStatusDetailsHTML()
]; ];
} }

View File

@ -1,49 +1,52 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requirePHPLib('judger'); requirePHPLib('judger');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { Auth::check() || redirectToLogin();
redirectToLogin();
$conds = [];
$config = [
'judge_time_hidden' => true,
'table_config' => [
'div_classes' => ['card', 'mb-3', 'table-responsive'],
'table_classes' => ['table', 'mb-0', 'uoj-table', 'text-center'],
]
];
$q_problem_id = UOJRequest::get('problem_id', 'validateUInt', null);
$q_submitter = UOJRequest::get('submitter', 'validateUsername', null);
$q_min_score = UOJRequest::get('min_score', 'validateUInt', null);
$q_max_score = UOJRequest::get('max_score', 'validateUInt', null);
$q_lang = UOJRequest::get('language', 'is_short_string', null);
if ($q_problem_id !== null) {
$problem = UOJProblem::query($q_problem_id);
if ($problem) {
$config['problem'] = $problem;
} }
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { $conds['problem_id'] = $q_problem_id;
become403Page(); }
} if ($q_submitter !== null) {
$conds['submitter'] = $q_submitter;
}
if ($q_min_score !== null) {
$conds[] = ['score', '>=', $q_min_score];
}
if ($q_max_score !== null) {
$conds[] = ['score', '<=', $q_max_score];
}
if ($q_lang != null) {
$conds['language'] = $q_lang;
}
$conds = array(); if (!$conds) {
$conds = '1';
$q_problem_id = isset($_GET['problem_id']) && validateUInt($_GET['problem_id']) ? $_GET['problem_id'] : null; }
$q_submitter = isset($_GET['submitter']) && validateUsername($_GET['submitter']) ? $_GET['submitter'] : null; ?>
$q_min_score = isset($_GET['min_score']) && validateUInt($_GET['min_score']) ? $_GET['min_score'] : null;
$q_max_score = isset($_GET['max_score']) && validateUInt($_GET['max_score']) ? $_GET['max_score'] : null;
$q_language = isset($_GET['language']) ? $_GET['language'] : null;
if ($q_problem_id != null) {
$conds[] = "problem_id = $q_problem_id";
}
if ($q_submitter != null) {
$conds[] = "submitter = '$q_submitter'";
}
if ($q_min_score != null) {
$conds[] = "score >= $q_min_score";
}
if ($q_max_score != null) {
$conds[] = "score <= $q_max_score";
}
if ($q_language != null) {
$conds[] = sprintf("language = '%s'", DB::escape($q_language));
}
$html_esc_q_language = htmlspecialchars($q_language);
if ($conds) {
$cond = join($conds, ' and ');
} else {
$cond = '1';
}
?>
<?php echoUOJPageHeader(UOJLocale::get('submissions')) ?> <?php echoUOJPageHeader(UOJLocale::get('submissions')) ?>
<h1 class="h2"> <h1>
<?= UOJLocale::get('submissions') ?> <?= UOJLocale::get('submissions') ?>
</h1> </h1>
@ -51,20 +54,20 @@
<form id="form-search" class="row gy-2 gx-3 align-items-end mb-3" target="_self" method="GET"> <form id="form-search" class="row gy-2 gx-3 align-items-end mb-3" target="_self" method="GET">
<div id="form-group-problem_id" class="col-auto"> <div id="form-group-problem_id" class="col-auto">
<label for="input-problem_id" class="form-label"> <label for="input-problem_id" class="form-label">
<?= UOJLocale::get('problems::problem id')?>: <?= UOJLocale::get('problems::problem id') ?>:
</label> </label>
<input type="text" class="form-control form-control-sm" name="problem_id" id="input-problem_id" value="<?= $q_problem_id ?>" style="width:4em" /> <input type="text" class="form-control form-control-sm" name="problem_id" id="input-problem_id" value="<?= $q_problem_id ?>" style="width:4em" />
</div> </div>
<div id="form-group-submitter" class="col-auto"> <div id="form-group-submitter" class="col-auto">
<label for="input-submitter" class="control-label"> <label for="input-submitter" class="control-label">
<?= UOJLocale::get('username')?>: <?= UOJLocale::get('username') ?>:
</label> </label>
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<input type="text" class="form-control form-control-sm" name="submitter" id="input-submitter" value="<?= $q_submitter ?>" maxlength="20" style="width:10em" /> <input type="text" class="form-control form-control-sm" name="submitter" id="input-submitter" value="<?= $q_submitter ?>" maxlength="20" style="width:10em" />
<?php if (Auth::check()): ?> <?php if (Auth::check()) : ?>
<a id="my-submissions" href="/submissions?submitter=<?= Auth::id() ?>" class="btn btn-outline-secondary btn-sm"> <a id="my-submissions" href="/submissions?submitter=<?= Auth::id() ?>" class="btn btn-outline-secondary btn-sm">
我的 我的
</a> </a>
<?php endif ?> <?php endif ?>
</div> </div>
<script> <script>
@ -77,7 +80,7 @@
</div> </div>
<div id="form-group-score" class="col-auto"> <div id="form-group-score" class="col-auto">
<label for="input-min_score" class="control-label"> <label for="input-min_score" class="control-label">
<?= UOJLocale::get('score range')?>: <?= UOJLocale::get('score range') ?>:
</label> </label>
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<input type="text" class="form-control" name="min_score" id="input-min_score" value="<?= $q_min_score ?>" maxlength="3" style="width:4em" placeholder="0" /> <input type="text" class="form-control" name="min_score" id="input-min_score" value="<?= $q_min_score ?>" maxlength="3" style="width:4em" placeholder="0" />
@ -86,31 +89,20 @@
</div> </div>
</div> </div>
<div id="form-group-language" class="col-auto"> <div id="form-group-language" class="col-auto">
<label for="input-language" class="control-label"><?= UOJLocale::get('problems::language')?>:</label> <label for="input-language" class="control-label"><?= UOJLocale::get('problems::language') ?>:</label>
<select class="form-select form-select-sm" id="input-language" name="language"> <select class="form-select form-select-sm" id="input-language" name="language">
<option value="">All</option> <option value="">All</option>
<?php foreach ($uojSupportedLanguages as $lang): ?> <?php foreach (UOJLang::$supported_languages as $name => $lang) : ?>
<option value="<?= HTML::escape($lang) ?>" <?= $lang == $q_language ? 'selected' : '' ?>><?= HTML::escape($lang) ?></option> <option value="<?= HTML::escape($name) ?>" <?= $name == $q_lang ? 'selected' : '' ?>><?= HTML::escape($lang) ?></option>
<?php endforeach ?> <?php endforeach ?>
</select> </select>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<button type="submit" id="submit-search" class="btn btn-secondary btn-sm ml-2"><?= UOJLocale::get('search')?></button> <button type="submit" id="submit-search" class="btn btn-secondary btn-sm ml-2"><?= UOJLocale::get('search') ?></button>
</div> </div>
</form> </form>
</div> </div>
<?php <?php echoSubmissionsList($conds, 'order by id desc', $config, Auth::user()) ?>
echoSubmissionsList($cond,
'order by id desc',
[
'judge_time_hidden' => '',
'table_config' => [
'div_classes' => ['card', 'mb-3', 'table-responsive'],
'table_classes' => ['table', 'mb-0', 'uoj-table', 'text-center'],
]
],
$myUser);
?>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

File diff suppressed because it is too large Load Diff

View File

@ -2,23 +2,15 @@
requireLib('bootstrap5'); requireLib('bootstrap5');
requireLib('calendar_heatmap'); requireLib('calendar_heatmap');
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { if (!Auth::check()) {
redirectToLogin(); redirectToLogin();
} }
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) { ($user = UOJUser::query($_GET['username'])) || UOJResponse::page404();
become403Page();
}
$username = $_GET['username'];
if (!validateUsername($username) || !($user = queryUser($username))) {
become404Page();
}
?> ?>
<?php echoUOJPageHeader($user['username'] . ' - ' . UOJLocale::get('user profile')) ?> <?php echoUOJPageHeader($user['username'] . ' - ' . UOJLocale::get('user profile')) ?>
<?php uojIncludeView('user-info', array('user' => $user, 'myUser' => $myUser)) ?> <?php uojIncludeView('user-info', ['user' => $user]) ?>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,189 +1,251 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requireLib('md5'); requireLib('md5');
requirePHPLib('form'); requirePHPLib('form');
if (!Auth::check()) { if (!Auth::check()) {
redirectToLogin(); redirectToLogin();
} }
if (!validateUsername($_GET['username']) || !($user = queryUser($_GET['username']))) { ($user = UOJUser::query($_GET['username'])) || UOJResponse::page404();
become404Page(); (isSuperUser(Auth::user()) || Auth::id() == $user['username']) || UOJResponse::page403();
} $extra = UOJUser::getExtra($user);
if (!isSuperUser($myUser) && $myUser['username'] != $user['username']) { if (isset($_GET['tab'])) {
become403Page(); $cur_tab = $_GET['tab'];
} } else {
$cur_tab = 'profile';
}
if (isset($_GET['tab'])) { $tabs_info = [
$cur_tab = $_GET['tab']; 'profile' => [
} else { 'name' => '<i class="bi bi-person-fill"></i> 个人资料',
$cur_tab = 'profile'; 'url' => "/user/{$user['username']}/edit/profile",
} ],
'password' => [
$tabs_info = [ 'name' => '<i class="bi bi-lock-fill"></i> 修改密码',
'profile' => [ 'url' => "/user/{$user['username']}/edit/password",
'name' => '<i class="bi bi-person-fill"></i> 个人资料', ],
'url' => "/user/{$user['username']}/edit/profile", 'privilege' => [
], 'name' => '<i class="bi bi-key-fill"></i> 特权',
'password' => [ 'url' => "/user/{$user['username']}/edit/privilege",
'name' => '<i class="bi bi-lock-fill"></i> 修改密码', ]
'url' => "/user/{$user['username']}/edit/password", ];
],
'privilege' => [
'name' => '<i class="bi bi-key-fill"></i> 特权',
'url' => "/user/{$user['username']}/edit/privilege",
]
];
if (!isset($tabs_info[$cur_tab])) {
become404Page();
}
if ($cur_tab == 'profile') { if (!isset($tabs_info[$cur_tab])) {
$update_profile_form = new UOJForm('update_profile'); become404Page();
$username = UOJLocale::get('username'); }
$avatar = UOJLocale::get('avatar');
$update_profile_form->appendHTML(<<<EOD if ($cur_tab == 'profile') {
$update_profile_form = new UOJBs4Form('update_profile');
$username = UOJLocale::get('username');
$avatar = UOJLocale::get('avatar');
$update_profile_form->appendHTML(<<<EOD
<div class="mb-3"> <div class="mb-3">
<label for="input-username" class="form-label">$username</label> <label for="input-username" class="form-label">$username</label>
<input type="text" class="form-control" id="input-username" aria-describedby="help-username" value="{$user['username']}" disabled> <input type="text" class="form-control" id="input-username" aria-describedby="help-username" value="{$user['username']}" disabled>
<div id="help-username" class="form-text">用户名不能被修改。</div> <div id="help-username" class="form-text">用户名不能被修改。</div>
</div> </div>
EOD); EOD);
if (isSuperUser($myUser)) { if (isSuperUser(Auth::user())) {
$update_profile_form->addVInput('realname', 'text', UOJLocale::get('user::real name'), $user['realname'], $update_profile_form->addVInput(
function($realname, &$vdata) { 'realname',
$vdata['realname'] = $realname; 'text',
UOJLocale::get('user::real name'),
$user['realname'],
function ($realname, &$vdata) {
$vdata['realname'] = $realname;
return ''; return '';
}, null); },
} else { null
$real_name = UOJLocale::get('user::real name'); );
$update_profile_form->appendHTML(<<<EOD } else {
$real_name = UOJLocale::get('user::real name');
$update_profile_form->appendHTML(<<<EOD
<div class="mb-3"> <div class="mb-3">
<label for="input-realname" class="form-label">$real_name</label> <label for="input-realname" class="form-label">$real_name</label>
<input type="text" class="form-control" id="input-realname" aria-describedby="help-realname" value="{$user['realname']}" disabled> <input type="text" class="form-control" id="input-realname" aria-describedby="help-realname" value="{$user['realname']}" disabled>
<div id="help-realname" class="form-text">只有管理员才能修改用户的真实姓名。</div> <div id="help-realname" class="form-text">只有管理员才能修改用户的真实姓名。</div>
</div> </div>
EOD); EOD);
} }
$update_profile_form->addVCheckboxes('avatar_source', [ $update_profile_form->addVCheckboxes('avatar_source', [
'gravatar' => 'Gravatar', 'gravatar' => 'Gravatar',
'qq' => 'QQ', 'qq' => 'QQ',
], UOJLocale::get('user::avatar source'), $user['avatar_source']); ], UOJLocale::get('user::avatar source'), $extra['avatar_source'] ?: 'gravatar');
$change_avatar_help = UOJLocale::get('change avatar help'); $change_avatar_help = UOJLocale::get('change avatar help');
$update_profile_form->appendHTML(<<<EOD $update_profile_form->appendHTML(<<<EOD
<div style="margin-top: -1.25rem;" class="mb-3 small text-muted"> <div style="margin-top: -1.25rem;" class="mb-3 small text-muted">
$change_avatar_help $change_avatar_help
</div> </div>
EOD); EOD);
$update_profile_form->addVInput('email', 'email', UOJLocale::get('email'), $user['email'], $update_profile_form->addVInput(
function($email, &$vdata) { 'email',
if (!validateEmail($email)) { 'email',
return 'Email 格式不合法。'; UOJLocale::get('email'),
} $user['email'] ?: '',
function ($email, &$vdata) {
if (!validateEmail($email)) {
return 'Email 格式不合法。';
}
$vdata['email'] = $email; $vdata['email'] = $email;
return '';
},
null
);
$update_profile_form->addVInput(
'qq',
'text',
UOJLocale::get('QQ'),
$user['qq'] == 0 ? '' : $user['qq'],
function ($qq, &$vdata) {
if ($qq && !validateQQ($qq)) {
return 'QQ 格式不合法。';
}
$vdata['qq'] = $qq;
return '';
},
null
);
$update_profile_form->addVInput(
'github',
'text',
'GitHub',
$extra['social']['github'] ?: '',
function ($github, &$vdata) {
if ($github && !validateGitHubUsername($github)) {
return 'GitHub 用户名不合法。';
}
$vdata['github'] = $github;
return '';
},
null
);
if (isSuperUser(Auth::user())) {
$update_profile_form->addVInput(
'school',
'text',
UOJLocale::get('school'),
$user['school'] ?: '',
function ($school, &$vdata) {
$vdata['school'] = $school;
return ''; return '';
}, null); },
$update_profile_form->addVInput('qq', 'text', UOJLocale::get('QQ'), $user['qq'] == 0 ? '' : $user['qq'], null
function($qq, &$vdata) { );
if ($qq && !validateQQ($qq)) { } else {
return 'QQ 格式不合法。'; $school = UOJLocale::get('school');
} $update_profile_form->appendHTML(<<<EOD
$vdata['qq'] = $qq;
return '';
}, null);
$update_profile_form->addVInput('github', 'text', 'GitHub', $user['github'],
function($github, &$vdata) {
if ($github && !validateGitHubUsername($github)) {
return 'GitHub 用户名不合法。';
}
$vdata['github'] = $github;
return '';
}, null);
if (isSuperUser($myUser)) {
$update_profile_form->addVInput('school', 'text', UOJLocale::get('school'), $user['school'],
function($school, &$vdata) {
$vdata['school'] = $school;
return '';
}, null);
} else {
$school = UOJLocale::get('school');
$update_profile_form->appendHTML(<<<EOD
<div class="mb-3"> <div class="mb-3">
<label for="input-school" class="form-label">$school</label> <label for="input-school" class="form-label">$school</label>
<input type="text" class="form-control" id="input-school" aria-describedby="help-school" value="{$user['school']}" disabled> <input type="text" class="form-control" id="input-school" aria-describedby="help-school" value="{$user['school']}" disabled>
<div id="help-school" class="form-text">只有管理员才能修改用户所属学校。</div> <div id="help-school" class="form-text">只有管理员才能修改用户所属学校。</div>
</div> </div>
EOD); EOD);
} }
$update_profile_form->addVCheckboxes('sex', [ $update_profile_form->addVCheckboxes('sex', [
'U' => UOJLocale::get('refuse to answer'), 'U' => UOJLocale::get('refuse to answer'),
'M' => UOJLocale::get('male'), 'M' => UOJLocale::get('male'),
'F' => UOJLocale::get('female'), 'F' => UOJLocale::get('female'),
], UOJLocale::get('sex'), $user['sex']); ], UOJLocale::get('sex'), $user['sex']);
$update_profile_form->addVInput('motto', 'text', UOJLocale::get('motto'), $user['motto'], $update_profile_form->addVInput(
function($motto, &$vdata) { 'motto',
if (!validateMotto($motto)) { 'text',
return '格言格式不合法'; UOJLocale::get('motto'),
} $user['motto'] ?: '',
function ($motto, &$vdata) {
$vdata['motto'] = $motto; if (!validateMotto($motto)) {
return '格言格式不合法';
return '';
}, null);
$update_profile_form->addVInput('codeforces_handle', 'text', UOJLocale::get('codeforces handle'), $user['codeforces_handle'],
function($codeforces_handle, &$vdata) {
if ($codeforces_handle && !validateUsername($codeforces_handle)) {
return 'Codeforces 用户名格式不合法。';
}
$vdata['codeforces_handle'] = $codeforces_handle;
return '';
}, null);
$update_profile_form->addVInput('website', 'text', UOJLocale::get('user::website'), $user['website'],
function($url, &$vdata) {
if ($url && !validateURL($url)) {
return '链接格式不合法。';
}
$vdata['website'] = $url;
return '';
}, null);
$update_profile_form->handle = function(&$vdata) use ($user, $myUser) {
$esc_email = DB::escape($vdata['email']);
$esc_qq = DB::escape($vdata['qq']);
$esc_github = DB::escape($vdata['github']);
$esc_sex = DB::escape($_POST['sex']);
$esc_motto = DB::escape($vdata['motto']);
$esc_codeforces_handle = DB::escape($vdata['codeforces_handle']);
$esc_website = DB::escape($vdata['website']);
$esc_avatar_source = DB::escape($_POST['avatar_source']);
if (isSuperUser($myUser)) {
$esc_realname = DB::escape($vdata['realname']);
$esc_school = DB::escape($vdata['school']);
DB::update("UPDATE user_info SET realname = '$esc_realname', school = '$esc_school' WHERE username = '{$user['username']}'");
} }
DB::update("UPDATE user_info SET email = '$esc_email', qq = '$esc_qq', sex = '$esc_sex', motto = '$esc_motto', codeforces_handle = '$esc_codeforces_handle', github = '$esc_github', website = '$esc_website', avatar_source = '$esc_avatar_source' WHERE username = '{$user['username']}'"); $vdata['motto'] = $motto;
dieWithJsonData(['status' => 'success']); return '';
}; },
$update_profile_form->submit_button_config['margin_class'] = 'mt-3'; null
$update_profile_form->submit_button_config['text'] = '更新'; );
$update_profile_form->setAjaxSubmit(<<<EOD $update_profile_form->addVInput(
'codeforces',
'text',
UOJLocale::get('codeforces handle'),
$extra['social']['codeforces'] ?: '',
function ($codeforces, &$vdata) {
if ($codeforces && !validateUsername($codeforces)) {
return 'Codeforces 用户名格式不合法。';
}
$vdata['codeforces'] = $codeforces;
return '';
},
null
);
$update_profile_form->addVInput(
'website',
'text',
UOJLocale::get('user::website'),
$extra['social']['website'] ?: '',
function ($url, &$vdata) {
if ($url && !validateURL($url)) {
return '链接格式不合法。';
}
$vdata['website'] = $url;
return '';
},
null
);
$update_profile_form->handle = function (&$vdata) use ($user) {
$data = [
'email' => $vdata['email'],
'qq' => $vdata['qq'],
'sex' => $_POST['sex'],
'motto' => $vdata['motto'],
];
if (isSuperUser(Auth::user())) {
$data['realname'] = $vdata['realname'];
$data['school'] = $vdata['school'];
}
DB::update([
"update user_info",
"set", $data,
"where", ["username" => $user['username']]
]);
DB::update([
"update user_info",
"set", [
'extra' => DB::json_set(
'extra',
'$.avatar_source',
$_POST['avatar_source'],
'$.social.github',
$vdata['github'],
'$.social.codeforces',
$vdata['codeforces'],
'$.social.website',
$vdata['website']
),
],
"where", ["username" => $user['username']]
]);
dieWithJsonData(['status' => 'success']);
};
$update_profile_form->submit_button_config['class_str'] = 'btn btn-secondary mt-3';
$update_profile_form->submit_button_config['text'] = '更新';
$update_profile_form->setAjaxSubmit(<<<EOD
function(res) { function(res) {
if (res.status === 'success') { if (res.status === 'success') {
$('#result-alert') $('#result-alert')
@ -202,307 +264,297 @@ function(res) {
$(window).scrollTop(0); $(window).scrollTop(0);
} }
EOD); EOD);
$update_profile_form->runAtServer(); $update_profile_form->runAtServer();
} elseif ($cur_tab == 'password') { } elseif ($cur_tab == 'password') {
if (isset($_POST['submit-change_password']) && $_POST['submit-change_password'] == 'change_password') { if (isset($_POST['submit-change_password']) && $_POST['submit-change_password'] == 'change_password') {
$old_password = $_POST['current_password']; $old_password = $_POST['current_password'];
$new_password = $_POST['new_password']; $new_password = $_POST['new_password'];
if (!validatePassword($old_password) || !checkPassword($user, $old_password)) { if (!validatePassword($old_password) || !checkPassword($user, $old_password)) {
dieWithJsonData(['status' => 'error', 'message' => '旧密码错误']); dieWithJsonData(['status' => 'error', 'message' => '旧密码错误']);
}
if (!validatePassword($new_password)) {
dieWithJsonData(['status' => 'error', 'message' => '新密码不合法']);
}
if ($old_password == $new_password) {
dieWithJsonData(['status' => 'error', 'message' => '新密码不能与旧密码相同']);
}
$password = getPasswordToStore($new_password, $user['username']);
DB::update("UPDATE `user_info` SET `password` = '$password' where `username` = '{$user['username']}'");
dieWithJsonData(['status' => 'success', 'message' => '密码修改成功']);
} }
} elseif ($cur_tab == 'privilege') {
if (isset($_POST['submit-privilege']) && $_POST['submit-privilege'] == 'privilege' && isSuperUser($myUser)) {
$user['usertype'] = 'student';
if ($_POST['user_type'] == 'teacher') { if (!validatePassword($new_password)) {
addUserType($user, 'teacher'); dieWithJsonData(['status' => 'error', 'message' => '新密码不合法']);
removeUserType($user, 'student');
} else {
addUserType($user, 'student');
}
if ($_POST['problem_uploader'] == 'yes') {
addUserType($user, 'problem_uploader');
}
if ($_POST['problem_manager'] == 'yes') {
addUserType($user, 'problem_manager');
}
if ($_POST['contest_judger'] == 'yes') {
addUserType($user, 'contest_judger');
}
if ($_POST['contest_only'] == 'yes') {
addUserType($user, 'contest_only');
}
DB::update("UPDATE `user_info` SET `usertype` = '{$user['usertype']}' where `username` = '{$user['username']}'");
dieWithJsonData(['status' => 'success', 'message' => '权限修改成功']);
} }
if ($old_password == $new_password) {
dieWithJsonData(['status' => 'error', 'message' => '新密码不能与旧密码相同']);
}
DB::update([
"update user_info",
"set", [
'password' => getPasswordToStore($new_password, $user['username']),
],
"where", ["username" => $user['username']]
]);
dieWithJsonData(['status' => 'success', 'message' => '密码修改成功']);
} }
} elseif ($cur_tab == 'privilege') {
if (isset($_POST['submit-privilege']) && $_POST['submit-privilege'] == 'privilege' && isSuperUser(Auth::user())) {
$user['usertype'] = 'student';
$pageTitle = $user['username'] == $myUser['username'] if ($_POST['user_type'] == 'teacher') {
? UOJLocale::get('modify my profile') addUserType($user, 'teacher');
: UOJLocale::get('modify his profile', $user['username']) removeUserType($user, 'student');
?> } else {
addUserType($user, 'student');
}
if ($_POST['problem_uploader'] == 'yes') {
addUserType($user, 'problem_uploader');
}
if ($_POST['problem_manager'] == 'yes') {
addUserType($user, 'problem_manager');
}
if ($_POST['contest_judger'] == 'yes') {
addUserType($user, 'contest_judger');
}
DB::update("UPDATE `user_info` SET `usertype` = '{$user['usertype']}' where `username` = '{$user['username']}'");
dieWithJsonData(['status' => 'success', 'message' => '权限修改成功']);
}
}
$pageTitle = $user['username'] == Auth::id()
? UOJLocale::get('modify my profile')
: UOJLocale::get('modify his profile', $user['username'])
?>
<?php echoUOJPageHeader($pageTitle) ?> <?php echoUOJPageHeader($pageTitle) ?>
<h1 class="h2"> <h1>
<?= $pageTitle ?> <?= $pageTitle ?>
</h1> </h1>
<div class="row mt-4"> <div class="row mt-4">
<!-- left col --> <!-- left col -->
<div class="col-md-3"> <div class="col-md-3">
<?= HTML::navListGroup($tabs_info, $cur_tab) ?> <?= HTML::navListGroup($tabs_info, $cur_tab) ?>
<a <a class="btn btn-light d-block mt-2 w-100 text-start text-primary" style="--bs-btn-hover-bg: #d3d4d570; --bs-btn-hover-border-color: transparent;" href="<?= HTML::url("/user/{$user['username']}") ?>">
class="btn btn-light d-block mt-2 w-100 text-start text-primary" <i class="bi bi-arrow-left"></i> 返回
style="--bs-btn-hover-bg: #d3d4d570; --bs-btn-hover-border-color: transparent;" </a>
href="<?= HTML::url("/user/{$user['username']}") ?>">
<i class="bi bi-arrow-left"></i> 返回
</a>
<?php if (isSuperUser($myUser) && $user['username'] != $myUser['username']): ?> <?php if ($user['username'] != Auth::id()) : ?>
<div class="alert alert-warning mt-3 small" role="alert"> <div class="alert alert-warning mt-3 small" role="alert">
您正在使用管理特权查看并编辑其它用户的资料。 您正在使用管理特权查看并编辑其它用户的资料。
</div> </div>
<?php endif ?> <?php endif ?>
</div>
<!-- end left col -->
<!-- right col -->
<div class="col-md-9">
<?php if ($cur_tab == 'profile'): ?>
<div class="card">
<div class="card-body">
<div id="result-alert" class="alert" role="alert" style="display: none"></div>
<?php $update_profile_form->printHTML() ?>
</div>
</div> </div>
<?php elseif ($cur_tab == 'password'): ?> <!-- end left col -->
<div class="card">
<div class="card-body"> <!-- right col -->
<div id="result-alert" class="alert" role="alert" style="display: none"></div> <div class="col-md-9">
<form method="post" id="form-change_password"> <?php if ($cur_tab == 'profile') : ?>
<div class="mb-3"> <div class="card">
<label for="input-current_password" class="form-label"> <div class="card-body">
<?= UOJLocale::get('current password') ?> <div id="result-alert" class="alert" role="alert" style="display: none"></div>
</label> <?php $update_profile_form->printHTML() ?>
<input type="password" class="form-control" id="input-current_password" placeholder="<?= UOJLocale::get('enter your password') ?>" maxlength="20">
<div id="help-current_password" class="invalid-feedback"></div>
</div> </div>
<div class="mb-3"> </div>
<label for="input-new_password" class="form-label"> <?php elseif ($cur_tab == 'password') : ?>
<?= UOJLocale::get('new password') ?> <div class="card">
</label> <div class="card-body">
<input type="password" class="form-control" id="input-new_password" placeholder="<?= UOJLocale::get('enter your new password') ?>" maxlength="20"> <div id="result-alert" class="alert" role="alert" style="display: none"></div>
<div id="help-new_password" class="invalid-feedback"></div> <form method="post" id="form-change_password">
</div> <div class="mb-3">
<div class="mb-3"> <label for="input-current_password" class="form-label">
<label for="input-confirm_password" class="form-label"> <?= UOJLocale::get('current password') ?>
<?= UOJLocale::get('confirm new password') ?> </label>
</label> <input type="password" class="form-control" id="input-current_password" placeholder="<?= UOJLocale::get('enter your password') ?>" maxlength="20">
<input type="password" class="form-control" id="input-confirm_password" placeholder="<?= UOJLocale::get('re-enter your new password') ?>" maxlength="20"> <div id="help-current_password" class="invalid-feedback"></div>
<div id="help-confirm_password" class="invalid-feedback"></div> </div>
</div> <div class="mb-3">
<?php if (isSuperUser($myUser) && $user['username'] != $myUser['username']): ?> <label for="input-new_password" class="form-label">
<div class="alert alert-warning mb-0" role="alert"> <?= UOJLocale::get('new password') ?>
如需重置其他用户的密码,请前往 <a href="/super_manage/users" class="alert-link">系统管理</a> 页面操作。 </label>
</div> <input type="password" class="form-control" id="input-new_password" placeholder="<?= UOJLocale::get('enter your new password') ?>" maxlength="20">
<?php endif ?> <div id="help-new_password" class="invalid-feedback"></div>
</div>
<div class="text-center"> <div class="mb-3">
<button type="submit" id="button-submit-change_password" name="submit-change_password" value="change_password" class="mt-3 btn btn-secondary">更新</button> <label for="input-confirm_password" class="form-label">
</div> <?= UOJLocale::get('confirm new password') ?>
</form> </label>
</div> <input type="password" class="form-control" id="input-confirm_password" placeholder="<?= UOJLocale::get('re-enter your new password') ?>" maxlength="20">
</div> <div id="help-confirm_password" class="invalid-feedback"></div>
<script> </div>
$('#form-change_password').submit(function() { <?php if (isSuperUser(Auth::user()) && $user['username'] != $myUser['username']) : ?>
var ok = true; <div class="alert alert-warning mb-0" role="alert">
如需重置其他用户的密码,请前往 <a href="/super_manage/users" class="alert-link">系统管理</a> 页面操作。
$('#result-alert').hide(); </div>
ok &= getFormErrorAndShowHelp('current_password', validatePassword);
ok &= getFormErrorAndShowHelp('new_password', validateSettingPassword);
if (ok) {
$.ajax({
method: 'POST',
data: {
'submit-change_password': 'change_password',
'current_password': md5($('#input-current_password').val(), "<?= getPasswordClientSalt() ?>"),
'new_password': md5($('#input-new_password').val(), "<?= getPasswordClientSalt() ?>"),
},
success: function(res) {
if (res.status === 'success') {
$('#result-alert')
.html('密码修改成功!')
.addClass('alert-success')
.removeClass('alert-danger')
.show();
} else {
$('#result-alert')
.html('密码修改失败。' + (res.message || ''))
.removeClass('alert-success')
.addClass('alert-danger')
.show();
}
$(window).scrollTop(0);
},
error: function() {
$('#result-alert')
.html('密码修改失败:请求失败。')
.removeClass('alert-success')
.addClass('alert-danger')
.show();
$(window).scrollTop(0);
}
});
}
return false;
});
</script>
<?php elseif ($cur_tab == 'privilege'): ?>
<div class="card">
<div class="card-body">
<div id="result-alert" class="alert" role="alert" style="display: none"></div>
<form id="form-privilege" method="post">
<?php if (isSuperUser($myUser)): ?>
<fieldset>
<?php else: ?>
<fieldset disabled>
<?php endif ?>
<div class="mb-3">
<span>
<?= UOJLocale::get('user::user group') ?>
</span>
<span class="d-inline-block ms-3">
<?php if ($user['usergroup'] == 'S'): ?>
<?= UOJLocale::get('user::super user') ?>
<?php elseif ($user['usergroup'] == 'B'): ?>
<?= UOJLocale::get('user::banned user') ?>
<?php else: ?>
<?= UOJLocale::get('user::normal user') ?>
<?php endif ?> <?php endif ?>
</span>
</div>
<div class="input-group mb-3">
<label for="input-user_type" class="form-label">
<?= UOJLocale::get('user::user type') ?>
</label>
<div class="form-check ms-3">
<input class="form-check-input" type="radio" name="user_type" value="student" id="input-user_type" <?= hasUserType($user, 'student') && !hasUserType($user, 'teacher') ? 'checked' : '' ?>>
<label class="form-check-label" for="input-user_type">
<?= UOJLocale::get('user::student') ?>
</label>
</div>
<div class="form-check ms-2">
<input class="form-check-input" type="radio" name="user_type" value="teacher" id="input-user_type_2" <?= hasUserType($user, 'teacher') ? 'checked' : '' ?>>
<label class="form-check-label" for="input-user_type_2">
<?= UOJLocale::get('user::teacher') ?>
</label>
</div>
</div>
<div class="form-check form-switch"> <div class="text-center">
<input class="form-check-input" type="checkbox" role="switch" name="problem_uploader" id="input-problem_uploader" <?= hasUserType($user, 'problem_uploader') ? 'checked' : '' ?>> <button type="submit" id="button-submit-change_password" name="submit-change_password" value="change_password" class="mt-3 btn btn-secondary">更新</button>
<label class="form-check-label" for="input-problem_uploader"> </div>
<?= UOJLocale::get('user::problem uploader') ?> </form>
</label>
</div> </div>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="problem_manager" id="input-problem_manager" <?= hasUserType($user, 'problem_manager') ? 'checked' : '' ?>>
<label class="form-check-label" for="input-problem_manager">
<?= UOJLocale::get('user::problem manager') ?>
</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="contest_judger" id="input-contest_judger" <?= hasUserType($user, 'contest_judger') ? 'checked' : '' ?>>
<label class="form-check-label" for="input-contest_judger">
<?= UOJLocale::get('user::contest judger') ?>
</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="contest_only" id="input-contest_only" <?= hasUserType($user, 'contest_only') ? 'checked' : '' ?>>
<label class="form-check-label" for="input-contest_only">
<?= UOJLocale::get('user::contest only') ?>
</label>
</div>
</fieldset>
<?php if (isSuperUser($myUser)): ?>
<div class="text-center">
<button type="submit" id="button-submit-privilege" name="submit-privilege" value="privilege" class="mt-3 btn btn-secondary">更新</button>
</div>
<?php endif ?>
</form>
<script> <script>
$('#form-privilege').submit(function(e) { $('#form-change_password').submit(function() {
var ok = true;
$('#result-alert').hide(); $('#result-alert').hide();
$.post('', { ok &= getFormErrorAndShowHelp('current_password', validatePassword);
user_type: $('input[name=user_type]:checked').val(), ok &= getFormErrorAndShowHelp('new_password', validateSettingPassword);
problem_uploader: $('input[name=problem_uploader]').prop('checked') ? 'yes' : 'no',
problem_manager: $('input[name=problem_manager]').prop('checked') ? 'yes' : 'no',
contest_judger: $('input[name=contest_judger]').prop('checked') ? 'yes' : 'no',
contest_only: $('input[name=contest_only]').prop('checked') ? 'yes' : 'no',
'submit-privilege': 'privilege',
}, function(res) {
if (res && res.status === 'success') {
$('#result-alert')
.html('权限修改成功!')
.addClass('alert-success')
.removeClass('alert-danger')
.show();
$(window).scrollTop(0); if (ok) {
} else { $.ajax({
$('#result-alert') method: 'POST',
.html('权限修改失败。' + (res.message || '')) data: {
.removeClass('alert-success') 'submit-change_password': 'change_password',
.addClass('alert-danger') 'current_password': md5($('#input-current_password').val(), "<?= getPasswordClientSalt() ?>"),
.show(); 'new_password': md5($('#input-new_password').val(), "<?= getPasswordClientSalt() ?>"),
},
success: function(res) {
if (res.status === 'success') {
$('#result-alert')
.html('密码修改成功!')
.addClass('alert-success')
.removeClass('alert-danger')
.show();
} else {
$('#result-alert')
.html('密码修改失败。' + (res.message || ''))
.removeClass('alert-success')
.addClass('alert-danger')
.show();
}
$(window).scrollTop(0); $(window).scrollTop(0);
} },
}); error: function() {
$('#result-alert')
.html('密码修改失败:请求失败。')
.removeClass('alert-success')
.addClass('alert-danger')
.show();
$(window).scrollTop(0);
}
});
}
return false; return false;
}); });
</script> </script>
</div> <?php elseif ($cur_tab == 'privilege') : ?>
<div class="card">
<div class="card-body">
<div id="result-alert" class="alert" role="alert" style="display: none"></div>
<form id="form-privilege" method="post">
<?php if (isSuperUser(Auth::user())) : ?>
<fieldset>
<?php else : ?>
<fieldset disabled>
<?php endif ?>
<div class="mb-3">
<span>
<?= UOJLocale::get('user::user group') ?>
</span>
<span class="d-inline-block ms-3">
<?php if ($user['usergroup'] == 'S') : ?>
<?= UOJLocale::get('user::super user') ?>
<?php elseif ($user['usergroup'] == 'B') : ?>
<?= UOJLocale::get('user::banned user') ?>
<?php else : ?>
<?= UOJLocale::get('user::normal user') ?>
<?php endif ?>
</span>
</div>
<div class="input-group mb-3">
<label for="input-user_type" class="form-label">
<?= UOJLocale::get('user::user type') ?>
</label>
<div class="form-check ms-3">
<input class="form-check-input" type="radio" name="user_type" value="student" id="input-user_type" <?= hasUserType($user, 'student') && !hasUserType($user, 'teacher') ? 'checked' : '' ?>>
<label class="form-check-label" for="input-user_type">
<?= UOJLocale::get('user::student') ?>
</label>
</div>
<div class="form-check ms-2">
<input class="form-check-input" type="radio" name="user_type" value="teacher" id="input-user_type_2" <?= hasUserType($user, 'teacher') ? 'checked' : '' ?>>
<label class="form-check-label" for="input-user_type_2">
<?= UOJLocale::get('user::teacher') ?>
</label>
</div>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="problem_uploader" id="input-problem_uploader" <?= hasUserType($user, 'problem_uploader') ? 'checked' : '' ?>>
<label class="form-check-label" for="input-problem_uploader">
<?= UOJLocale::get('user::problem uploader') ?>
</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="problem_manager" id="input-problem_manager" <?= hasUserType($user, 'problem_manager') ? 'checked' : '' ?>>
<label class="form-check-label" for="input-problem_manager">
<?= UOJLocale::get('user::problem manager') ?>
</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="contest_judger" id="input-contest_judger" <?= hasUserType($user, 'contest_judger') ? 'checked' : '' ?>>
<label class="form-check-label" for="input-contest_judger">
<?= UOJLocale::get('user::contest judger') ?>
</label>
</div>
</fieldset>
<?php if (isSuperUser(Auth::user())) : ?>
<div class="text-center">
<button type="submit" id="button-submit-privilege" name="submit-privilege" value="privilege" class="mt-3 btn btn-secondary">更新</button>
</div>
<?php endif ?>
</form>
<script>
$('#form-privilege').submit(function(e) {
$('#result-alert').hide();
$.post('', {
user_type: $('input[name=user_type]:checked').val(),
problem_uploader: $('input[name=problem_uploader]').prop('checked') ? 'yes' : 'no',
problem_manager: $('input[name=problem_manager]').prop('checked') ? 'yes' : 'no',
contest_judger: $('input[name=contest_judger]').prop('checked') ? 'yes' : 'no',
'submit-privilege': 'privilege',
}, function(res) {
if (res && res.status === 'success') {
$('#result-alert')
.html('权限修改成功!')
.addClass('alert-success')
.removeClass('alert-danger')
.show();
$(window).scrollTop(0);
} else {
$('#result-alert')
.html('权限修改失败。' + (res.message || ''))
.removeClass('alert-success')
.addClass('alert-danger')
.show();
$(window).scrollTop(0);
}
});
return false;
});
</script>
</div>
</div>
<?php endif ?>
<!-- end right col -->
</div> </div>
<?php endif ?>
<!-- end right col -->
</div>
</div> </div>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -1,12 +1,8 @@
<?php <?php
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) { if (!Auth::check()) {
redirectToLogin(); redirectToLogin();
} }
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) {
become403Page();
}
function handleMsgPost() { function handleMsgPost() {
global $myUser; global $myUser;
if (!isset($_POST['receiver'])) { if (!isset($_POST['receiver'])) {

View File

@ -1,131 +1,121 @@
<?php <?php
requireLib('bootstrap5'); requireLib('bootstrap5');
requirePHPLib('form'); requirePHPLib('form');
if (!Auth::check()) { Auth::check() || redirectToLogin();
redirectToLogin();
}
if (!validateUsername($_GET['username']) || !($user = queryUser($_GET['username']))) { if (!($user = UOJUser::query($_GET['username']))) {
become404Page(); become404Page();
} }
if (!isSuperUser($myUser) && $myUser['username'] != $user['username']) { if (!isSuperUser(Auth::user()) && Auth::id() != $user['username']) {
become403Page(); become403Page();
} }
function newDeleteSystemMsgForm($id) { function newDeleteSystemMsgForm($id) {
$form = new UOJForm('remove_system_msg_' . $id); $form = new UOJBs4Form('remove_system_msg_' . $id);
$form->addHidden("msg_id", $id, function($msg_id) {
global $user;
if (!validateUInt($msg_id)) { $form->addHidden("msg_id", $id, function ($msg_id) {
return '消息 ID 不是有效的数字'; global $user;
}
$msg = DB::selectFirst("select * from user_system_msg where id = {$msg_id}"); if (!validateUInt($msg_id)) {
if (!$msg || $msg['receiver'] != $user['username']) { return '消息 ID 不是有效的数字';
return '消息不存在';
}
return '';
}, null);
$form->handle = function() {
$msg_id = $_POST["msg_id"];
DB::delete("delete from user_system_msg where id = {$msg_id}");
};
$form->submit_button_config['text'] = '删除';
$form->submit_button_config['margin_class'] = 'mt-0';
$form->submit_button_config['class_str'] = 'btn btn-link text-decoration-none text-danger p-0';
$form->submit_button_config['align'] = 'inline';
$form->submit_button_config['smart_confirm'] = '';
return $form;
}
$pag_config = [
'page_len' => 10,
'col_names' => ['*'],
'table_name' => 'user_system_msg',
'cond' => "receiver = '{$user['username']}'",
'tail' => 'order by send_time desc',
];
$pag = new Paginator($pag_config);
$system_msgs = [];
foreach ($pag->get() as $idx => $msg) {
$system_msgs[$idx] = $msg;
if (isSuperUser($myUser)) {
$delete_form = newDeleteSystemMsgForm($msg['id']);
$delete_form->runAtServer();
$system_msgs[$idx]['delete_form'] = $delete_form;
} }
$msg = DB::selectFirst("select * from user_system_msg where id = {$msg_id}");
if (!$msg || $msg['receiver'] != $user['username']) {
return '消息不存在';
}
return '';
}, null);
$form->handle = function () {
$msg_id = $_POST["msg_id"];
DB::delete("delete from user_system_msg where id = {$msg_id}");
};
$form->submit_button_config['text'] = '删除';
$form->submit_button_config['class_str'] = 'btn btn-link text-decoration-none text-danger p-0 mt-0';
$form->submit_button_config['align'] = 'inline';
$form->submit_button_config['smart_confirm'] = '';
return $form;
}
$pag_config = [
'page_len' => 10,
'col_names' => ['*'],
'table_name' => 'user_system_msg',
'cond' => "receiver = '{$user['username']}'",
'tail' => 'order by send_time desc',
];
$pag = new Paginator($pag_config);
$system_msgs = [];
foreach ($pag->get() as $idx => $msg) {
$system_msgs[$idx] = $msg;
if (isSuperUser($myUser)) {
$delete_form = newDeleteSystemMsgForm($msg['id']);
$delete_form->runAtServer();
$system_msgs[$idx]['delete_form'] = $delete_form;
} }
?> }
?>
<?php echoUOJPageHeader('系统消息') ?> <?php echoUOJPageHeader('系统消息') ?>
<div class="row"> <div class="row">
<!-- left col --> <!-- left col -->
<div class="col-lg-9"> <div class="col-lg-9">
<h1>系统消息</h1>
<h1 class="h2"> <div class="card mb-3">
系统消息 <ul class="list-group list-group-flush">
</h1> <?php foreach ($system_msgs as $msg) : ?>
<li class="list-group-item">
<div class="mb-2 d-flex justify-content-between">
<div>
<?php if ($msg['title']) : ?>
<h4 class="d-inline"><?= $msg['title'] ?></h4>
<?php endif ?>
<div class="card mb-3"> <span class="text-muted small ms-2 d-inline-block">
<ul class="list-group list-group-flush"> 发送时间: <time><?= $msg['send_time'] ?></time>
<?php foreach ($system_msgs as $msg): ?> </span>
<li class="list-group-item </div>
<?= $msg['read_time'] ? '' : 'bg-warning bg-opacity-25' ?>
">
<div class="mb-2 d-flex justify-content-between">
<div>
<?php if ($msg['title']): ?>
<h4 class="d-inline"><?= $msg['title'] ?></h4>
<?php endif ?>
<span class="text-muted small ms-2 d-inline-block"> <?php if (isset($msg['delete_form'])) : ?>
发送时间: <time><?= $msg['send_time'] ?></time> <?php $msg['delete_form']->printHTML() ?>
</span> <?php endif ?>
</div> </div>
<?php if (isset($msg['delete_form'])): ?> <div><?= $msg['content'] ?></div>
<?php $msg['delete_form']->printHTML() ?> </li>
<?php endforeach ?>
<?php if ($pag->isEmpty()) : ?>
<div class="text-center">
<?= UOJLocale::get('none') ?>
</div>
<?php endif ?> <?php endif ?>
</div> </ul>
<div><?= $msg['content'] ?></div>
</li>
<?php endforeach ?>
<?php if ($pag->isEmpty()): ?>
<div class="text-center">
<?= UOJLocale::get('none') ?>
</div> </div>
<?php endif ?>
</ul>
</div>
<?= $pag->pagination() ?> <?= $pag->pagination() ?>
</div>
<!-- end left col -->
<!-- right col -->
<aside class="col-lg-3 mt-3 mt-lg-0">
<?php uojIncludeView('sidebar') ?>
</aside>
<!-- end right col -->
</div>
<?php <?php
if (Auth::id() == $user['username']) { if (Auth::id() == $user['username']) {
DB::update("update user_system_msg set read_time = now() where receiver = '" . $user['username'] . "'"); DB::update("update user_system_msg set read_time = now() where receiver = '" . $user['username'] . "'");
} }
?> ?>
</div>
<!-- end left col -->
<!-- right col -->
<aside class="col-lg-3 mt-3 mt-lg-0">
<?php uojIncludeView('sidebar', array()) ?>
</aside>
<!-- end right col -->
</div>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -3,7 +3,7 @@ define("CONTEST_NOT_STARTED", 0);
define("CONTEST_IN_PROGRESS", 1); define("CONTEST_IN_PROGRESS", 1);
define("CONTEST_PENDING_FINAL_TEST", 2); define("CONTEST_PENDING_FINAL_TEST", 2);
define("CONTEST_TESTING", 10); define("CONTEST_TESTING", 10);
define("CONTEST_FINISHED", 20); define("CONTEST_FINISHED", 20);
function genMoreContestInfo(&$contest) { function genMoreContestInfo(&$contest) {
$contest['start_time_str'] = $contest['start_time']; $contest['start_time_str'] = $contest['start_time'];
@ -11,7 +11,7 @@ function genMoreContestInfo(&$contest) {
$contest['end_time'] = clone $contest['start_time']; $contest['end_time'] = clone $contest['start_time'];
$contest['end_time']->add(new DateInterval("PT${contest['last_min']}M")); $contest['end_time']->add(new DateInterval("PT${contest['last_min']}M"));
$contest['end_time_str'] = $contest['end_time']->format('Y-m-d H:i:s'); $contest['end_time_str'] = $contest['end_time']->format('Y-m-d H:i:s');
if ($contest['status'] == 'unfinished') { if ($contest['status'] == 'unfinished') {
if (UOJTime::$time_now < $contest['start_time']) { if (UOJTime::$time_now < $contest['start_time']) {
$contest['cur_progress'] = CONTEST_NOT_STARTED; $contest['cur_progress'] = CONTEST_NOT_STARTED;
@ -26,37 +26,110 @@ function genMoreContestInfo(&$contest) {
$contest['cur_progress'] = CONTEST_FINISHED; $contest['cur_progress'] = CONTEST_FINISHED;
} }
$contest['extra_config'] = json_decode($contest['extra_config'], true); $contest['extra_config'] = json_decode($contest['extra_config'], true);
if (!isset($contest['extra_config']['standings_version'])) { if (!isset($contest['extra_config']['standings_version'])) {
$contest['extra_config']['standings_version'] = 2; $contest['extra_config']['standings_version'] = 2;
} }
} }
function updateContestPlayerNum($contest) { function updateContestPlayerNum($contest) {
DB::update("update contests set player_num = (select count(*) from contests_registrants where contest_id = {$contest['id']}) where id = {$contest['id']}"); DB::update([
"update contests",
"set", [
"player_num" => DB::rawbracket([
"select count(*) from contests_registrants",
"where", ["contest_id" => $contest['id']]
])
], "where", ["id" => $contest['id']]
]);
} }
// return value: ['problems' => $problems, 'data' => $data, 'people' => $people]
// problems: pos => id // problems: pos => id
// data : id, submit_time, submitter, problem_pos, score //
// people : username // for individual competition:
function queryContestData($contest, $config = array(), $is_after_contest_query = false) { // people : username, realname
// for team competition:
// people : username, null, ['team_name' => team_name, 'members' => members]
//
// for OI/IOI contest:
// data : id, submit_time, submitter, problem_pos, score
// for ACM contest:
// data : id, submit_time (plus penalty), submitter, problem_pos, score, cnt, n_failures
// if the contest is not finished, then cnt = null, n_failures = null;
// otherwise, cnt is the total number of submission of this subitter for this problem
// (by the time of getting 100, including the first submission with score 100)
// n_failures is the number of failure attempts of this submitter for this problem
function queryContestData($contest, $config = []) {
mergeConfig($config, [ mergeConfig($config, [
'pre_final' => false 'pre_final' => false,
'after_contest' => false,
]); ]);
$problems = []; $problems = [];
$prob_pos = []; $prob_pos = [];
$n_problems = 0; $n_problems = 0;
$result = DB::query("select problem_id from contests_problems where contest_id = {$contest['id']} order by dfn, problem_id"); $res = DB::selectAll([
while ($row = DB::fetch($result, MYSQLI_NUM)) { "select problem_id from contests_problems",
"where", ["contest_id" => $contest['id']],
"order by level, problem_id"
], DB::NUM);
foreach ($res as $row) {
$prob_pos[$problems[] = (int)$row[0]] = $n_problems++; $prob_pos[$problems[] = (int)$row[0]] = $n_problems++;
} }
if ($contest['extra_config']['basic_rule'] == 'OI' || $contest['extra_config']['basic_rule'] == 'IOI') {
$data = queryOIorIOIContestSubmissionData($contest, $problems, $prob_pos, $config);
} elseif ($contest['extra_config']['basic_rule'] == 'ACM') {
$data = queryACMContestSubmissionData($contest, $problems, $prob_pos, $config);
}
$people = [];
if ($contest['extra_config']['individual_or_team'] == 'individual') {
$people = DB::selectAll([
"select contests_registrants.username, user_info.realname from contests_registrants",
"inner join user_info on contests_registrants.username = user_info.username",
"where", [
"contest_id" => $contest['id'],
"has_participated" => 1
]
], DB::NUM);
} elseif ($contest['extra_config']['individual_or_team'] == 'team') {
$res = DB::selectAll([
"select user_info.username, null, user_info.extra from contests_registrants, user_info",
"where", [
"contest_id" => $contest['id'],
"has_participated" => 1,
"contests_registrants.username = user_info.username"
]
], DB::NUM);
foreach ($res as $row) {
$extra = json_decode($row[2], true);
$row[2] = [
'team_name' => $extra['acm']['team_name'],
'members' => $extra['acm']['members']
];
$people[] = $row;
}
}
return ['problems' => $problems, 'data' => $data, 'people' => $people];
}
function queryOIorIOIContestSubmissionData($contest, $problems, $prob_pos, $config = []) {
$data = []; $data = [];
if ($config['pre_final']) {
$result = DB::query("select id, submit_time, submitter, problem_id, result from submissions" $use_final_res = $config['pre_final'] && $contest['extra_config']['basic_rule'] == 'OI';
." where contest_id = {$contest['id']} and score is not null order by id");
while ($row = DB::fetch($result, MYSQLI_NUM)) { if ($use_final_res) {
$res = DB::selectAll([
"select id, submit_time, submitter, problem_id, result from submissions",
"where", [
"contest_id" => $contest['id'],
["score", "is not", null]
], "order by id"
], DB::NUM);
foreach ($res as $row) {
$r = json_decode($row[4], true); $r = json_decode($row[4], true);
if (!isset($r['final_result'])) { if (!isset($r['final_result'])) {
continue; continue;
@ -68,71 +141,233 @@ function queryContestData($contest, $config = array(), $is_after_contest_query =
} }
} else { } else {
if ($contest['cur_progress'] < CONTEST_FINISHED) { if ($contest['cur_progress'] < CONTEST_FINISHED) {
$result = DB::query("select id, submit_time, submitter, problem_id, score from submissions" $res = DB::selectAll([
." where contest_id = {$contest['id']} and score is not null order by id"); "select id, submit_time, submitter, problem_id, score from submissions",
} elseif ($is_after_contest_query == true) { "where", [
$result = DB::query("select id, submit_time, submitter, problem_id, score from submissions order by score"); "contest_id" => $contest['id'],
["score", "is not", null]
], "order by id"
], DB::NUM);
} elseif ($config['after_contest']) {
$res = DB::selectAll([
"select id, submit_time, submitter, problem_id, score from submissions",
"where", [
["problem_id", "in", DB::rawtuple($problems)],
], "order by score",
], DB::NUM);
} else { } else {
$result = DB::query("select submission_id, date_add('{$contest['start_time_str']}', interval penalty second)," $esc_start_time_str = DB::escape($contest['start_time_str']);
." submitter, problem_id, score from contests_submissions where contest_id = {$contest['id']}"); $res = DB::selectAll([
"select submission_id, date_add('{$esc_start_time_str}', interval penalty second), submitter, problem_id, score from contests_submissions",
"where", ["contest_id" => $contest['id']],
], DB::NUM);
} }
while ($row = DB::fetch($result, MYSQLI_NUM)) { foreach ($res as $row) {
$row[0] = (int)$row[0]; $row[0] = (int)$row[0];
$row[3] = $prob_pos[$row[3]]; $row[3] = $prob_pos[$row[3]];
$row[4] = (int)$row[4]; $row[4] = (int)$row[4];
$data[] = $row; $data[] = $row;
} }
} }
return $data;
$people = [];
$result = DB::query("select a.username, b.realname from contests_registrants a inner join user_info b on a.username = b.username where a.contest_id = {$contest['id']} and a.has_participated = 1");
while ($row = DB::fetch($result, MYSQLI_NUM)) {
$people[] = $row;
}
return ['problems' => $problems, 'data' => $data, 'people' => $people];
} }
function calcStandings($contest, $contest_data, &$score, &$standings, $update_contests_submissions = false, $show_reviews = false) { function queryACMContestSubmissionData($contest, $problems, $prob_pos, $config = []) {
// score: username, problem_pos => score, penalty, id, ?review $data = [];
$score = array();
$username_or_empty = Auth::id();
if (!isset($username_or_empty)) {
$username_or_empty = '';
}
$actual_score = UOJSubmission::sqlForActualScore();
$visible_score = 'if(' . DB::land(['hide_score_to_others' => 1, 'submitter' => $username_or_empty]) . ', hidden_score, score)';
if ($config['pre_final']) {
$res = DB::selectAll([
"select id, submit_time, submitter, problem_id, $actual_score as actual_score, null, null from submissions",
"where", [
"contest_id" => $contest['id'],
[$actual_score, "is not", null]
], "order by id"
], DB::NUM);
} else {
if ($contest['cur_progress'] < CONTEST_FINISHED) {
$res = DB::selectAll([
"select id, submit_time, submitter, problem_id, $visible_score as visible_score, null, null from submissions",
"where", [
"contest_id" => $contest['id'],
DB::lor([
[$visible_score, "is not", null],
DB::land([
"hide_score_to_others" => 1,
["submitter", "!=", $username_or_empty]
])
])
], "order by id"
], DB::NUM);
} else {
$esc_start_time_str = DB::escape($contest['start_time_str']);
$res = DB::selectAll([
"select submission_id, date_add('{$esc_start_time_str}', interval penalty second), submitter, problem_id, score, cnt, n_failures from contests_submissions",
"where", ["contest_id" => $contest['id']],
], DB::NUM);
}
}
foreach ($res as $row) {
$row[0] = (int)$row[0];
$row[3] = $prob_pos[$row[3]];
if (isset($row[4])) {
$row[4] = (int)$row[4];
}
if (isset($row[5])) {
$row[5] = (int)$row[5];
}
if (isset($row[6])) {
$row[6] = (int)$row[6];
}
$data[] = $row;
}
return $data;
}
// standings: rank => score, penalty, [username, realname], virtual_rank
function calcStandings($contest, $contest_data, &$score, &$standings, $cfg = []) {
$cfg += [
'update_contests_submissions' => false,
];
// score for OI: username, problem_pos => score, penalty, id
// score for ACM: username, problem_pos => score, penalty, id, cnt, n_failures, n_frozen
$score = [];
$n_people = count($contest_data['people']); $n_people = count($contest_data['people']);
$n_problems = count($contest_data['problems']); $n_problems = count($contest_data['problems']);
foreach ($contest_data['people'] as $person) { foreach ($contest_data['people'] as $person) {
$score[$person[0]] = array(); $score[$person[0]] = [];
} }
foreach ($contest_data['data'] as $submission) {
$penalty = (new DateTime($submission[1]))->getTimestamp() - $contest['start_time']->getTimestamp(); if ($contest['extra_config']['basic_rule'] === 'OI') {
if ($contest['extra_config']['standings_version'] >= 2) { foreach ($contest_data['data'] as $sub) {
if ($submission[4] == 0) { $penalty = (new DateTime($sub[1]))->getTimestamp() - $contest['start_time']->getTimestamp();
$penalty = 0; if ($contest['extra_config']['standings_version'] >= 2) {
if ($sub[4] == 0) {
$penalty = 0;
}
} }
$score[$sub[2]][$sub[3]] = array($sub[4], $penalty, $sub[0]);
}
} else if ($contest['extra_config']['basic_rule'] === 'ACM') {
// sub: id, submit_time, submitter, problem_pos, score
// id, submit_time (plus penalty), submitter, problem_pos, score, cnt, n_failures
foreach ($contest_data['data'] as $sub) {
if (!isset($score[$sub[2]][$sub[3]])) {
$score[$sub[2]][$sub[3]] = [];
}
$score[$sub[2]][$sub[3]][] = $sub;
} }
$score[$submission[2]][$submission[3]] = array($submission[4], $penalty, $submission[0]);
}
if ($show_reviews) {
$parsedown = HTML::parsedown();
$purifier = HTML::purifier_inline();
foreach ($contest_data['people'] as $person) { foreach ($contest_data['people'] as $person) {
foreach ($contest_data['problems'] as $key => $problem) { $uname = $person[0];
$review_result = DB::selectFirst("select content from contests_reviews where contest_id = {$contest['id']} and problem_id = {$problem} and poster = '{$person[0]}'"); for ($pr = 0; $pr < $n_problems; $pr++) {
if (isset($score[$uname][$pr])) {
// username, problem_pos => score, penalty, id, cnt, n_failures, n_frozen
$final_scr = null;
$penalty = 0;
$key_sub = null;
$cnt = 0;
$n_failures = 0;
$n_frozen = 0;
if (!isset($score[$person[0]][$key])) { if (isset($score[$uname][$pr][0][5])) { // the stored contest data is used
$score[$person[0]][$key] = array(0, 0, 0); $sub = $score[$uname][$pr][0];
} $final_scr = $sub[4];
$penalty = (new DateTime($sub[1]))->getTimestamp() - $contest['start_time']->getTimestamp();
$key_sub = $sub;
$cnt = $sub[5];
$n_failures = $sub[6];
$n_frozen = 0;
} else {
for ($i = 0; $i < count($score[$uname][$pr]); $i++) {
$sub = $score[$uname][$pr][$i];
$cnt++;
if (!isset($sub[4])) {
$n_frozen++;
} elseif (!isset($final_scr) || $final_scr < $sub[4]) {
$final_scr = $sub[4];
if ($final_scr == 100) {
break;
}
}
}
if ($review_result['content']) { if (!isset($final_scr)) {
$score[$person[0]][$key][] = $purifier->purify($parsedown->line($review_result['content'])); $key_sub = end($score[$uname][$pr]);
} else if ($final_scr == 0) {
for ($i = 0; $i < count($score[$uname][$pr]); $i++) {
$sub = $score[$uname][$pr][$i];
if (!isset($sub[4])) {
break;
} else {
$n_failures++;
$key_sub = $sub;
}
}
list($final_scr, $penalty) = calcACMScoreAndPenaltyForOneProblem(
$contest,
$contest_data['problems'][$pr],
$key_sub,
$n_failures
);
} else {
$scr_set = [];
for ($i = 0; $i < count($score[$uname][$pr]); $i++) {
$sub = $score[$uname][$pr][$i];
if ($sub[4] > 0 && $sub[4] != 97 && !isset($scr_set[$sub[4]])) {
$scr_set[$sub[4]] = true;
} else {
$n_failures++;
}
if ($sub[4] === $final_scr) {
$key_sub = $sub;
break;
}
}
list($final_scr, $penalty) = calcACMScoreAndPenaltyForOneProblem(
$contest,
$contest_data['problems'][$pr],
$key_sub,
$n_failures
);
}
}
$score[$uname][$pr] = [
$final_scr,
$penalty,
$key_sub[0],
$cnt,
$n_failures,
$n_frozen
];
} }
} }
} }
} else if ($contest['extra_config']['basic_rule'] === 'IOI') {
foreach ($contest_data['data'] as $sub) {
$penalty = (new DateTime($sub[1]))->getTimestamp() - $contest['start_time']->getTimestamp();
if ($sub[4] == 0) {
$penalty = 0;
}
if (!isset($score[$sub[2]][$sub[3]]) || $score[$sub[2]][$sub[3]][0] < $sub[4]) {
$score[$sub[2]][$sub[3]] = array($sub[4], $penalty, $sub[0]);
}
}
} }
// standings: rank => score, penalty, [username, realname], virtual_rank, ?review // standings: rank => score, penalty, [username, realname], virtual_rank, ?review
$standings = array(); $standings = [];
foreach ($contest_data['people'] as $person) { foreach ($contest_data['people'] as $person) {
$cur = array(0, 0, $person); $cur = array(0, 0, $person);
for ($i = 0; $i < $n_problems; $i++) { for ($i = 0; $i < $n_problems; $i++) {
@ -140,52 +375,71 @@ function calcStandings($contest, $contest_data, &$score, &$standings, $update_co
$cur_row = $score[$person[0]][$i]; $cur_row = $score[$person[0]][$i];
$cur[0] += $cur_row[0]; $cur[0] += $cur_row[0];
$cur[1] += $cur_row[1]; $cur[1] += $cur_row[1];
if ($update_contests_submissions) { if ($cfg['update_contests_submissions']) {
DB::insert("replace into contests_submissions (contest_id, submitter, problem_id, submission_id, score, penalty) values ({$contest['id']}, '{$person[0]}', {$contest_data['problems'][$i]}, {$cur_row[2]}, {$cur_row[0]}, {$cur_row[1]})"); DB::insert([
"replace into contests_submissions",
"(contest_id, submitter, problem_id, submission_id, score, penalty, cnt, n_failures)",
"values", DB::tuple([
$contest['id'], $person[0], $contest_data['problems'][$i], $cur_row[2],
$cur_row[0], $cur_row[1],
isset($cur_row[3]) ? $cur_row[3] : null,
isset($cur_row[4]) ? $cur_row[4] : null
])
]);
} }
} }
} }
if ($show_reviews) {
$review_result = DB::selectFirst("select content from contests_reviews where contest_id = {$contest['id']} and problem_id = -1 and poster = '{$person[0]}'");
if ($review_result['content']) {
$cur[] = $purifier->purify($parsedown->line($review_result['content']));
}
}
$standings[] = $cur; $standings[] = $cur;
} }
usort($standings, function($lhs, $rhs) { usort($standings, function ($lhs, $rhs) {
if ($lhs[0] != $rhs[0]) { if ($lhs[0] != $rhs[0]) {
return $rhs[0] - $lhs[0]; return $rhs[0] - $lhs[0];
} elseif ($lhs[1] != $rhs[1]) { } else if ($lhs[1] != $rhs[1]) {
return $lhs[1] - $rhs[1]; return $lhs[1] - $rhs[1];
} else { } else {
return strcmp($lhs[2][0], $rhs[2][0]); return strcmp($lhs[2][0], $rhs[2][0]);
} }
}); });
$is_same_rank = function($lhs, $rhs) { $is_same_rank = function ($lhs, $rhs) {
return $lhs[0] == $rhs[0] && $lhs[1] == $rhs[1]; return $lhs[0] == $rhs[0] && $lhs[1] == $rhs[1];
}; };
for ($i = 0; $i < $n_people; $i++) { for ($i = 0; $i < $n_people; $i++) {
if ($i == 0 || !$is_same_rank($standings[$i - 1], $standings[$i])) { if ($i == 0 || !$is_same_rank($standings[$i - 1], $standings[$i])) {
if ($show_reviews && count($standings[$i]) == 4) { $standings[$i][] = $i + 1;
$standings[$i][] = $standings[$i][3];
$standings[$i][3] = $i + 1;
} else {
$standings[$i][] = $i + 1;
}
} else { } else {
if ($show_reviews && count($standings[$i]) == 4) { $standings[$i][] = $standings[$i - 1][3];
$standings[$i][] = $standings[$i][3];
$standings[$i][3] = $standings[$i - 1][3];
} else {
$standings[$i][] = $standings[$i - 1][3];
}
} }
} }
} }
function calcACMScoreAndPenaltyForOneProblem($contest, $problem_id, $sub, $n_failures) {
if (isset($contest['extra_config']['bonus']["problem_{$problem_id}"])) {
if ($sub[4] === 100) {
return [0, -60 * 20];
} else {
return [0, 0];
}
} else {
$penalty = (new DateTime($sub[1]))->getTimestamp() - $contest['start_time']->getTimestamp();
$penalty += $n_failures * 60 * 20;
if ($sub[4] === 0) {
$penalty = 0;
}
return [$sub[4], $penalty];
}
}
function getContestBlogLink($contest, $title) {
if (!isset($contest['extra_config']['links'])) {
return null;
}
foreach ($contest['extra_config']['links'] as $link) {
if ($link[0] === $title) {
return '/blogs/' . $link[1];
}
}
return null;
}

View File

@ -1,359 +1,508 @@
<?php <?php
// Actually, these things should be done by main_judger so that the code would be much simpler. // Actually, these things should be done by main_judger so that the code would be much simpler.
// However, this lib exists due to some history issues. // However, this lib exists due to some history issues.
function dataNewProblem($id) { function dataNewProblem($id) {
mkdir("/var/uoj_data/upload/$id"); mkdir("/var/uoj_data/upload/$id");
mkdir("/var/uoj_data/$id"); mkdir("/var/uoj_data/$id");
exec("cd /var/uoj_data; rm $id.zip; zip $id.zip $id -r -q"); UOJLocalRun::execAnd([
['cd', '/var/uoj_data'],
['rm', "$id.zip"],
['zip', "$id.zip", $id, '-r', '-q']
]);
}
function dataClearProblemData($problem) {
$id = $problem['id'];
if (!validateUInt($id)) {
UOJLog::error("dataClearProblemData: hacker detected");
return "invalid problem id";
} }
class UOJProblemConfException extends Exception { UOJLocalRun::exec(['rm', "/var/uoj_data/$id", '-r']);
public function __construct($message) { UOJLocalRun::exec(['rm', "/var/uoj_data/upload/$id", '-r']);
parent::__construct("<strong>problem.conf</strong> : $message"); dataNewProblem($id);
}
class SyncProblemDataHandler {
private UOJProblem $problem;
private $user;
private int $id;
private string $upload_dir, $data_dir, $prepare_dir;
private $requirement, $problem_extra_config;
private $problem_conf, $final_problem_conf;
private $allow_files;
public function retryMsg() {
return '请等待上一次数据上传或同步操作结束后重试';
}
public function __construct($problem_info, $user = null) {
$this->problem = new UOJProblem($problem_info);
$this->user = $user;
if (!validateUInt($this->problem->info['id'])) {
UOJLog::error("SyncProblemDataHandler: hacker detected");
return;
}
$this->id = (int)$this->problem->info['id'];
$this->data_dir = "/var/uoj_data/{$this->id}";
$this->prepare_dir = "/var/uoj_data/prepare_{$this->id}";
$this->upload_dir = "/var/uoj_data/upload/{$this->id}";
}
/**
* $type can be either LOCK_SH or LOCK_EX
*/
private function lock($type, $func) {
$ret = FS::lock_file("/var/uoj_data/{$this->id}_lock", $type, $func);
return $ret === false ? $this->retryMsg() : $ret;
}
private function check_conf_on($name) {
return isset($this->problem_conf[$name]) && $this->problem_conf[$name] == 'on';
}
private function create_prepare_folder() {
return mkdir($this->prepare_dir, 0755);
}
private function remove_prepare_folder() {
return UOJLocalRun::exec(['rm', $this->prepare_dir, '-rf']);
}
private function copy_to_prepare($file_name) {
if (!isset($this->allow_files[$file_name])) {
throw new UOJFileNotFoundException($file_name);
}
$src = "{$this->upload_dir}/$file_name";
$dest = "{$this->prepare_dir}/$file_name";
if (file_exists($dest)) {
return;
}
if (isset($this->problem_extra_config['dont_use_formatter']) || !is_file("{$this->upload_dir}/$file_name")) {
exec("cp $src $dest -r", $output, $ret);
$ret = UOJLocalRun::exec(['cp', $src, $dest, '-r']);
} else {
$ret = UOJLocalRun::formatter($src, $dest);
}
if ($ret === false) {
throw new UOJFileNotFoundException($file_name);
} }
} }
class UOJFileNotFoundException extends Exception {
public function __construct($file_name) { private function copy_file_to_prepare($file_name) {
parent::__construct("file <strong>" . htmlspecialchars($file_name) . '</strong> not found'); if (!isset($this->allow_files[$file_name]) || !is_file("{$this->upload_dir}/$file_name")) {
throw new UOJFileNotFoundException($file_name);
} }
$this->copy_to_prepare($file_name);
} }
function dataClearProblemData($problem) { private function copy_source_code_to_prepare($code_name) { // file name without suffix
$id = $problem['id']; $src = UOJLang::findSourceCode($code_name, $this->upload_dir);
if (!validateUInt($id)) {
error_log("dataClearProblemData: hacker detected"); if ($src === false) {
return "invalid problem id"; throw new UOJFileNotFoundException($code_name);
} }
exec("rm /var/uoj_data/upload/$id -r"); $this->copy_to_prepare($src['path']);
exec("rm /var/uoj_data/$id -r");
dataNewProblem($id);
} }
class SyncProblemDataHandler { private function compile_at_prepare($name, $config = []) {
private $problem, $user; $include_path = UOJLocalRun::$judger_include_path;
private $upload_dir, $data_dir, $prepare_dir;
private $requirement, $problem_extra_config; $src = UOJLang::findSourceCode($name, $this->prepare_dir);
private $problem_conf, $final_problem_conf;
private $allow_files; if (isset($config['path'])) {
if (rename("{$this->prepare_dir}/{$src['path']}", "{$this->prepare_dir}/{$config['path']}/{$src['path']}") === false) {
public function __construct($problem, $user) { throw new Exception("<strong>$name</strong> : move failed");
$this->problem = $problem;
$this->user = $user;
}
private function check_conf_on($name) {
return isset($this->problem_conf[$name]) && $this->problem_conf[$name] == 'on';
}
private function copy_to_prepare($file_name) {
global $uojMainJudgerWorkPath;
if (!isset($this->allow_files[$file_name])) {
throw new UOJFileNotFoundException($file_name);
} }
$src = escapeshellarg("{$this->upload_dir}/$file_name"); $work_path = "{$this->prepare_dir}/{$config['path']}";
$dest = escapeshellarg("{$this->prepare_dir}/$file_name"); } else {
if (isset($this->problem_extra_config['dont_use_formatter']) || !is_file("{$this->upload_dir}/$file_name")) { $work_path = $this->prepare_dir;
exec("cp $src $dest -r", $output, $ret); }
$compile_options = [
['custom', UOJLocalRun::$judger_run_path]
];
$runp_options = [
['in', '/dev/null'],
['out', 'stderr'],
['err', "{$this->prepare_dir}/compiler_result.txt"],
['tl', 60],
['ml', 512],
['ol', 64],
['type', 'compiler'],
['work-path', $work_path],
];
if (!empty($config['need_include_header'])) {
$compile_options[] = ['cinclude', $include_path];
$runp_options[] = ['add-readable-raw', "{$include_path}/"];
}
if (!empty($config['implementer'])) {
$compile_options[] = ['impl', $config['implementer']];
}
$res = UOJLocalRun::compile($name, $compile_options, $runp_options);
$this->final_problem_conf["{$name}_run_type"] = UOJLang::getRunTypeFromLanguage($src['lang']);
$rstype = isset($res['rstype']) ? $res['rstype'] : 7;
if ($rstype != 0 || $res['exit_code'] != 0) {
if ($rstype == 0) {
throw new Exception("<strong>$name</strong> : compile error<pre>\n" . HTML::escape(uojFilePreview("{$this->prepare_dir}/compiler_result.txt", 10000)) . "\n</pre>");
} elseif ($rstype == 7) {
throw new Exception("<strong>$name</strong> : compile error. No comment");
} else { } else {
exec("$uojMainJudgerWorkPath/run/formatter <$src >$dest", $output, $ret); throw new Exception("<strong>$name</strong> : compile error. Compiler " . judgerCodeStr($rstype));
}
if ($ret) {
throw new UOJFileNotFoundException($file_name);
} }
} }
private function copy_file_to_prepare($file_name) {
global $uojMainJudgerWorkPath; unlink("{$this->prepare_dir}/compiler_result.txt");
if (!isset($this->allow_files[$file_name]) || !is_file("{$this->upload_dir}/$file_name")) {
throw new UOJFileNotFoundException($file_name); if (isset($config['path'])) {
} rename("{$this->prepare_dir}/{$config['path']}/{$src['path']}", "{$this->prepare_dir}/{$src['path']}");
$this->copy_to_prepare($file_name); rename("{$this->prepare_dir}/{$config['path']}/$name", "{$this->prepare_dir}/$name");
} }
private function compile_at_prepare($name, $config = array()) { }
global $uojMainJudgerWorkPath;
$include_path = "$uojMainJudgerWorkPath/include"; private function makefile_at_prepare() {
$include_path = UOJLocalRun::$judger_include_path;
if (!isset($config['src'])) {
$config['src'] = "$name.cpp"; $res = UOJLocalRun::exec(['/usr/bin/make', "INCLUDE_PATH={$include_path}"], [
} ['in', '/dev/null'],
['out', 'stderr'],
if (isset($config['path'])) { ['err', "{$this->prepare_dir}/makefile_result.txt"],
exec("mv {$this->prepare_dir}/$name.cpp {$this->prepare_dir}/{$config['path']}/$name.cpp"); ['tl', 60],
$work_path = "{$this->prepare_dir}/{$config['path']}"; ['ml', 512],
['ol', 64],
['type', 'compiler'],
['work-path', $this->prepare_dir],
['add-readable-raw', "{$include_path}/"]
]);
$rstype = isset($res['rstype']) ? $res['rstype'] : 7;
if ($rstype != 0 || $res['exit_code'] != 0) {
if ($rstype == 0) {
throw new Exception("<strong>Makefile</strong> : compile error<pre>\n" . HTML::escape(uojFilePreview("{$this->prepare_dir}/makefile_result.txt", 10000)) . "\n</pre>");
} elseif ($rstype == 7) {
throw new Exception("<strong>Makefile</strong> : compile error. No comment");
} else { } else {
$work_path = $this->prepare_dir; throw new Exception("<strong>Makefile</strong> : compile error. Compiler " . judgerCodeStr($rstype));
}
$cmd_prefix = "$uojMainJudgerWorkPath/run/run_program >{$this->prepare_dir}/run_compiler_result.txt --in=/dev/null --out=stderr --err={$this->prepare_dir}/compiler_result.txt --tl=10 --ml=512 --ol=64 --type=compiler --work-path={$work_path}";
if (isset($config['need_include_header']) && $config['need_include_header']) {
exec("$cmd_prefix --add-readable-raw=$include_path/ /usr/bin/g++ -o $name {$config['src']} -I$include_path -lm -O2 -DONLINE_JUDGE");
} else {
exec("$cmd_prefix /usr/bin/g++ -o $name {$config['src']} -lm -O2 -DONLINE_JUDGE");
}
$fp = fopen("{$this->prepare_dir}/run_compiler_result.txt", "r");
if (fscanf($fp, '%d %d %d %d', $rs, $used_time, $used_memory, $exit_code) != 4) {
$rs = 7;
}
fclose($fp);
unlink("{$this->prepare_dir}/run_compiler_result.txt");
if ($rs != 0 || $exit_code != 0) {
if ($rs == 0) {
throw new Exception("<strong>$name</strong> : compile error<pre>\n" . uojFilePreview("{$this->prepare_dir}/compiler_result.txt", 100) . "\n</pre>");
} elseif ($rs == 7) {
throw new Exception("<strong>$name</strong> : compile error. No comment");
} else {
throw new Exception("<strong>$name</strong> : compile error. Compiler " . judgerCodeStr($rs));
}
}
unlink("{$this->prepare_dir}/compiler_result.txt");
if (isset($config['path'])) {
exec("mv {$this->prepare_dir}/{$config['path']}/$name.cpp {$this->prepare_dir}/$name.cpp");
exec("mv {$this->prepare_dir}/{$config['path']}/$name {$this->prepare_dir}/$name");
} }
} }
private function makefile_at_prepare() {
global $uojMainJudgerWorkPath; unlink("{$this->prepare_dir}/makefile_result.txt");
}
$include_path = "$uojMainJudgerWorkPath/include";
$cmd_prefix = "$uojMainJudgerWorkPath/run/run_program >{$this->prepare_dir}/run_makefile_result.txt --in=/dev/null --out=stderr --err={$this->prepare_dir}/makefile_result.txt --tl=10 --ml=512 --ol=64 --type=compiler --work-path={$this->prepare_dir}"; public function _updateProblemConf($new_problem_conf) {
exec("$cmd_prefix --add-readable-raw=$include_path/ /usr/bin/make INCLUDE_PATH=$include_path"); try {
putUOJConf("{$this->data_dir}/problem.conf", $new_problem_conf);
$fp = fopen("{$this->prepare_dir}/run_makefile_result.txt", "r"); return '';
if (fscanf($fp, '%d %d %d %d', $rs, $used_time, $used_memory, $exit_code) != 4) { } catch (Exception $e) {
$rs = 7; return $e->getMessage();
}
fclose($fp);
unlink("{$this->prepare_dir}/run_makefile_result.txt");
if ($rs != 0 || $exit_code != 0) {
if ($rs == 0) {
throw new Exception("<strong>Makefile</strong> : compile error<pre>\n" . uojFilePreview("{$this->prepare_dir}/makefile_result.txt", 100) . "\n</pre>");
} elseif ($rs == 7) {
throw new Exception("<strong>Makefile</strong> : compile error. No comment");
} else {
throw new Exception("<strong>Makefile</strong> : compile error. Compiler " . judgerCodeStr($rs));
}
}
unlink("{$this->prepare_dir}/makefile_result.txt");
} }
}
public function handle() { public function updateProblemConf($new_problem_conf) {
$id = $this->problem['id']; return $this->lock(LOCK_EX, fn () => $this->_updateProblemConf($new_problem_conf));
if (!validateUInt($id)) { }
error_log("dataSyncProblemData: hacker detected");
return "invalid problem id"; private function _addHackPoint($uploaded_input_file, $uploaded_output_file, $reason) {
try {
switch ($this->problem->getExtraConfig('add_hack_as')) {
case 'test':
$key_num = 'n_tests';
$msg = 'add new test';
$gen_in_name = 'getUOJProblemInputFileName';
$gen_out_name = 'getUOJProblemOutputFileName';
break;
case 'ex_test':
$key_num = 'n_ex_tests';
$msg = 'add new extra test';
$gen_in_name = 'getUOJProblemExtraInputFileName';
$gen_out_name = 'getUOJProblemExtraOutputFileName';
break;
default:
return 'add hack to data failed: add_hack_as should be either "ex_test" or "test"';
} }
$this->upload_dir = "/var/uoj_data/upload/$id"; $new_problem_conf = $this->problem->getProblemConfArray();
$this->data_dir = "/var/uoj_data/$id"; if ($new_problem_conf == -1 || $new_problem_conf == -2) {
$this->prepare_dir = "/var/uoj_data/prepare_$id"; return $new_problem_conf;
}
$new_problem_conf[$key_num] = getUOJConfVal($new_problem_conf, $key_num, 0) + 1;
if (file_exists($this->prepare_dir)) { putUOJConf("{$this->upload_dir}/problem.conf", $new_problem_conf);
return "please wait until the last sync finish";
$new_input_name = $gen_in_name($new_problem_conf, $new_problem_conf[$key_num]);
$new_output_name = $gen_out_name($new_problem_conf, $new_problem_conf[$key_num]);
if (!copy($uploaded_input_file, "{$this->upload_dir}/$new_input_name")) {
return "input file not found";
}
if (!copy($uploaded_output_file, "{$this->upload_dir}/$new_output_name")) {
return "output file not found";
}
} catch (Exception $e) {
return $e->getMessage();
}
$ret = $this->_sync();
if ($ret !== '') {
return "hack successfully but sync failed: $ret";
}
if (isset($reason['hack_url'])) {
UOJSystemUpdate::updateProblem($this->problem, [
'text' => 'Hack 成功,自动添加数据',
'url' => $reason['hack_url']
]);
}
UOJSubmission::rejudgeProblemAC($this->problem, [
'reason_text' => $reason['rejudge'],
'requestor' => ''
]);
return '';
}
public function addHackPoint($uploaded_input_file, $uploaded_output_file, $reason = []) {
return $this->lock(LOCK_EX, fn () => $this->_addHackPoint($uploaded_input_file, $uploaded_output_file, $reason));
}
public function fast_hackable_check() {
if (!$this->problem->info['hackable']) {
return;
}
if (!$this->check_conf_on('use_builtin_judger')) {
return;
}
if ($this->check_conf_on('submit_answer')) {
throw new UOJProblemConfException("提交答案题不可 Hack请先停用本题的 Hack 功能。");
} else {
if (UOJLang::findSourceCode('std', $this->upload_dir) === false) {
throw new UOJProblemConfException("找不到本题的 std。请上传 std 代码文件,或停用本题的 Hack 功能。");
}
if (UOJLang::findSourceCode('val', $this->upload_dir) === false) {
throw new UOJProblemConfException("找不到本题的 val。请上传 val 代码文件,或停用本题的 Hack 功能。");
}
}
}
private function _sync() {
try {
if (!$this->create_prepare_folder()) {
throw new UOJSyncFailedException('创建临时文件夹失败');
} }
try { $this->requirement = [];
$this->requirement = array(); $this->problem_extra_config = $this->problem->getExtraConfig();;
$this->problem_extra_config = json_decode($this->problem['extra_config'], true); if (!is_file("{$this->upload_dir}/problem.conf")) {
throw new UOJFileNotFoundException("problem.conf");
}
mkdir($this->prepare_dir, 0755); $this->problem_conf = getUOJConf("{$this->upload_dir}/problem.conf");
if (!is_file("{$this->upload_dir}/problem.conf")) { $this->final_problem_conf = $this->problem_conf;
throw new UOJFileNotFoundException("problem.conf"); if ($this->problem_conf === -1) {
throw new UOJFileNotFoundException("problem.conf");
} elseif ($this->problem_conf === -2) {
throw new UOJProblemConfException("syntax error");
}
$this->allow_files = array_flip(FS::scandir($this->upload_dir));
$zip_file = new ZipArchive();
if ($zip_file->open("{$this->prepare_dir}/download.zip", ZipArchive::CREATE) !== true) {
throw new Exception("<strong>download.zip</strong> : failed to create the zip file");
}
if (isset($this->allow_files['require']) && is_dir("{$this->upload_dir}/require")) {
$this->copy_to_prepare('require');
}
if (isset($this->allow_files['testlib.h']) && is_file("{$this->upload_dir}/testlib.h")) {
$this->copy_file_to_prepare('testlib.h');
}
$this->fast_hackable_check();
if ($this->check_conf_on('use_builtin_judger')) {
$n_tests = getUOJConfVal($this->problem_conf, 'n_tests', 10);
if (!validateUInt($n_tests) || $n_tests <= 0) {
throw new UOJProblemConfException("n_tests must be a positive integer");
}
for ($num = 1; $num <= $n_tests; $num++) {
$input_file_name = getUOJProblemInputFileName($this->problem_conf, $num);
$output_file_name = getUOJProblemOutputFileName($this->problem_conf, $num);
$this->copy_file_to_prepare($input_file_name);
$this->copy_file_to_prepare($output_file_name);
} }
$this->problem_conf = getUOJConf("{$this->upload_dir}/problem.conf"); if (!$this->check_conf_on('interaction_mode')) {
$this->final_problem_conf = $this->problem_conf; if (isset($this->problem_conf['use_builtin_checker'])) {
if ($this->problem_conf === -1) { if (!preg_match('/^[a-zA-Z0-9_]{1,20}$/', $this->problem_conf['use_builtin_checker'])) {
throw new UOJFileNotFoundException("problem.conf"); throw new Exception("<strong>" . HTML::escape($this->problem_conf['use_builtin_checker']) . "</strong> is not a valid checker");
} elseif ($this->problem_conf === -2) { }
throw new UOJProblemConfException("syntax error"); } else {
} $this->copy_source_code_to_prepare('chk');
$this->compile_at_prepare('chk', ['need_include_header' => true]);
$this->allow_files = array_flip(array_filter(scandir($this->upload_dir), function($x) {
return $x !== '.' && $x !== '..';
}));
$zip_file = new ZipArchive();
if ($zip_file->open("{$this->prepare_dir}/download.zip", ZipArchive::CREATE) !== true) {
throw new Exception("<strong>download.zip</strong> : failed to create the zip file");
}
if (isset($this->allow_files['require']) && is_dir("{$this->upload_dir}/require")) {
$this->copy_to_prepare('require');
}
if ($this->check_conf_on('use_builtin_judger')) {
$n_tests = getUOJConfVal($this->problem_conf, 'n_tests', 10);
if (!validateUInt($n_tests) || $n_tests <= 0) {
throw new UOJProblemConfException("n_tests must be a positive integer");
} }
}
if ($this->check_conf_on('submit_answer')) {
if (!isset($this->problem_extra_config['dont_download_input'])) {
for ($num = 1; $num <= $n_tests; $num++) {
$input_file_name = getUOJProblemInputFileName($this->problem_conf, $num);
$zip_file->addFile("{$this->prepare_dir}/$input_file_name", "$input_file_name");
}
}
$n_output_files = 0;
for ($num = 1; $num <= $n_tests; $num++) { for ($num = 1; $num <= $n_tests; $num++) {
$input_file_name = getUOJProblemInputFileName($this->problem_conf, $num); $output_file_id = getUOJConfVal($this->problem_conf, ["output_file_id_{$num}", "output_file_id"], "$num");
if (!validateUInt($output_file_id) || $output_file_id < 0 || $output_file_id > $n_tests) {
throw new UOJProblemConfException("output_file_id/output_file_id_{$num} must be in [1, n_tests]");
}
$n_output_files = max($n_output_files, $output_file_id);
}
for ($num = 1; $num <= $n_output_files; $num++) {
$output_file_name = getUOJProblemOutputFileName($this->problem_conf, $num); $output_file_name = getUOJProblemOutputFileName($this->problem_conf, $num);
$this->requirement[] = ['name' => "output$num", 'type' => 'text', 'file_name' => $output_file_name];
}
} else {
$n_ex_tests = getUOJConfVal($this->problem_conf, 'n_ex_tests', 0);
if (!validateUInt($n_ex_tests) || $n_ex_tests < 0) {
throw new UOJProblemConfException('n_ex_tests must be a non-negative integer. Current value: ' . HTML::escape($n_ex_tests));
}
for ($num = 1; $num <= $n_ex_tests; $num++) {
$input_file_name = getUOJProblemExtraInputFileName($this->problem_conf, $num);
$output_file_name = getUOJProblemExtraOutputFileName($this->problem_conf, $num);
$this->copy_file_to_prepare($input_file_name); $this->copy_file_to_prepare($input_file_name);
$this->copy_file_to_prepare($output_file_name); $this->copy_file_to_prepare($output_file_name);
} }
if (!$this->check_conf_on('interaction_mode')) { if ($this->problem->info['hackable']) {
if (isset($this->problem_conf['use_builtin_checker'])) { $this->copy_source_code_to_prepare('std');
if (!preg_match('/^[a-zA-Z0-9_]{1,20}$/', $this->problem_conf['use_builtin_checker'])) { if (isset($this->problem_conf['with_implementer']) && $this->problem_conf['with_implementer'] == 'on') {
throw new Exception("<strong>" . htmlspecialchars($this->problem_conf['use_builtin_checker']) . "</strong> is not a valid checker"); $this->compile_at_prepare('std', [
} 'implementer' => 'implementer',
'path' => 'require'
]);
} else { } else {
$this->copy_file_to_prepare('chk.cpp'); $this->compile_at_prepare('std');
$this->compile_at_prepare('chk', array('need_include_header' => true));
} }
$this->copy_source_code_to_prepare('val');
$this->compile_at_prepare('val', ['need_include_header' => true]);
} }
if ($this->check_conf_on('submit_answer')) {
if ($this->problem['hackable']) {
throw new UOJProblemConfException("the problem can't be hackable if submit_answer is on");
}
for ($num = 1; $num <= $n_tests; $num++) { if ($this->check_conf_on('interaction_mode')) {
$input_file_name = getUOJProblemInputFileName($this->problem_conf, $num); $this->copy_source_code_to_prepare('interactor');
$output_file_name = getUOJProblemOutputFileName($this->problem_conf, $num); $this->compile_at_prepare('interactor', ['need_include_header' => true]);
}
if (!isset($this->problem_extra_config['dont_download_input'])) {
$zip_file->addFile("{$this->prepare_dir}/$input_file_name", "$input_file_name");
}
$this->requirement[] = array('name' => "output$num", 'type' => 'text', 'file_name' => $output_file_name); $n_sample_tests = getUOJConfVal($this->problem_conf, 'n_sample_tests', $n_tests);
} if (!validateUInt($n_sample_tests) || $n_sample_tests < 0) {
} else { throw new UOJProblemConfException('n_sample_tests must be a non-negative integer. Current value: ' . HTML::escape($n_sample_tests));
$n_ex_tests = getUOJConfVal($this->problem_conf, 'n_ex_tests', 0); }
if (!validateUInt($n_ex_tests) || $n_ex_tests < 0) { if ($n_sample_tests > $n_ex_tests) {
throw new UOJProblemConfException("n_ex_tests must be a non-negative integer"); throw new UOJProblemConfException("n_sample_tests can't be greater than n_ex_tests");
} }
for ($num = 1; $num <= $n_ex_tests; $num++) { if (!isset($this->problem_extra_config['dont_download_sample'])) {
for ($num = 1; $num <= $n_sample_tests; $num++) {
$input_file_name = getUOJProblemExtraInputFileName($this->problem_conf, $num); $input_file_name = getUOJProblemExtraInputFileName($this->problem_conf, $num);
$output_file_name = getUOJProblemExtraOutputFileName($this->problem_conf, $num); $output_file_name = getUOJProblemExtraOutputFileName($this->problem_conf, $num);
$zip_file->addFile("{$this->prepare_dir}/{$input_file_name}", "$input_file_name");
$this->copy_file_to_prepare($input_file_name); if (!isset($this->problem_extra_config['dont_download_sample_output'])) {
$this->copy_file_to_prepare($output_file_name); $zip_file->addFile("{$this->prepare_dir}/{$output_file_name}", "$output_file_name");
}
if ($this->problem['hackable']) {
$this->copy_file_to_prepare('std.cpp');
if (isset($this->problem_conf['with_implementer']) && $this->problem_conf['with_implementer'] == 'on') {
$this->compile_at_prepare('std',
array(
'src' => 'implementer.cpp std.cpp',
'path' => 'require'
)
);
} else {
$this->compile_at_prepare('std');
}
$this->copy_file_to_prepare('val.cpp');
$this->compile_at_prepare('val', array('need_include_header' => true));
}
if ($this->check_conf_on('interaction_mode')) {
$this->copy_file_to_prepare('interactor.cpp');
$this->compile_at_prepare('interactor', array('need_include_header' => true));
}
$n_sample_tests = getUOJConfVal($this->problem_conf, 'n_sample_tests', $n_tests);
if (!validateUInt($n_sample_tests) || $n_sample_tests < 0) {
throw new UOJProblemConfException("n_sample_tests must be a non-negative integer");
}
if ($n_sample_tests > $n_ex_tests) {
throw new UOJProblemConfException("n_sample_tests can't be greater than n_ex_tests");
}
if (!isset($this->problem_extra_config['dont_download_sample'])) {
for ($num = 1; $num <= $n_sample_tests; $num++) {
$input_file_name = getUOJProblemExtraInputFileName($this->problem_conf, $num);
$output_file_name = getUOJProblemExtraOutputFileName($this->problem_conf, $num);
$zip_file->addFile("{$this->prepare_dir}/{$input_file_name}", "$input_file_name");
if (!isset($this->problem_extra_config['dont_download_sample_output'])) {
$zip_file->addFile("{$this->prepare_dir}/{$output_file_name}", "$output_file_name");
}
} }
} }
$this->requirement[] = array('name' => 'answer', 'type' => 'source code', 'file_name' => 'answer.code');
} }
$this->requirement[] = ['name' => 'answer', 'type' => 'source code', 'file_name' => 'answer.code'];
}
} else {
if (!isSuperUser($this->user)) {
throw new UOJProblemConfException("use_builtin_judger must be on.");
} else { } else {
if (!isSuperUser($this->user)) { foreach ($this->allow_files as $file_name => $file_num) {
throw new UOJProblemConfException("use_builtin_judger must be on."); $this->copy_to_prepare($file_name);
} else { }
foreach ($this->allow_files as $file_name => $file_num) { $this->makefile_at_prepare();
$this->copy_to_prepare($file_name);
} $this->requirement[] = ['name' => 'answer', 'type' => 'source code', 'file_name' => 'answer.code'];
$this->makefile_at_prepare(); }
}
$this->requirement[] = array('name' => 'answer', 'type' => 'source code', 'file_name' => 'answer.code'); putUOJConf("{$this->prepare_dir}/problem.conf", $this->final_problem_conf);
if (isset($this->allow_files['download']) && is_dir("{$this->upload_dir}/download")) {
$download_dir = "{$this->upload_dir}/download";
foreach (FS::scandir_r($download_dir) as $file_name) {
if (is_file("{$download_dir}/{$file_name}")) {
$zip_file->addFile("{$download_dir}/{$file_name}", $file_name);
} }
} }
putUOJConf("{$this->prepare_dir}/problem.conf", $this->final_problem_conf);
if (isset($this->allow_files['download']) && is_dir("{$this->upload_dir}/download")) {
foreach (scandir("{$this->upload_dir}/download") as $file_name) {
if (is_file("{$this->upload_dir}/download/{$file_name}")) {
$zip_file->addFile("{$this->upload_dir}/download/{$file_name}", $file_name);
}
}
}
$zip_file->close();
$orig_requirement = json_decode($this->problem['submission_requirement'], true);
if (!$orig_requirement) {
$esc_requirement = DB::escape(json_encode($this->requirement));
DB::update("update problems set submission_requirement = '$esc_requirement' where id = $id");
}
} catch (Exception $e) {
exec("rm {$this->prepare_dir} -r");
return $e->getMessage();
} }
exec("rm {$this->data_dir} -r"); $zip_file->close();
rename($this->prepare_dir, $this->data_dir);
exec("cd /var/uoj_data; rm $id.zip; zip $id.zip $id -r -q");
return ''; $orig_requirement = $this->problem->getSubmissionRequirement();
} if (!$orig_requirement) {
} DB::update([
"update problems",
function dataSyncProblemData($problem, $user = null) { "set", ["submission_requirement" => json_encode($this->requirement)],
return (new SyncProblemDataHandler($problem, $user))->handle(); "where", ["id" => $this->id]
} ]);
function dataAddExtraTest($problem, $input_file_name, $output_file_name) { }
$id = $problem['id'];
$cur_dir = "/var/uoj_data/upload/$id"; UOJSystemUpdate::updateProblemInternally($this->problem, [
'text' => 'sync',
$problem_conf = getUOJConf("{$cur_dir}/problem.conf"); 'requestor' => Auth::check() ? Auth::id() : null
if ($problem_conf == -1 || $problem_conf == -2) { ]);
return $problem_conf; } catch (Exception $e) {
$this->remove_prepare_folder();
return $e->getMessage();
} }
$problem_conf['n_ex_tests'] = getUOJConfVal($problem_conf, 'n_ex_tests', 0) + 1;
UOJLocalRun::exec(['rm', $this->data_dir, '-r']);
$new_input_name = getUOJProblemExtraInputFileName($problem_conf, $problem_conf['n_ex_tests']); rename($this->prepare_dir, $this->data_dir);
$new_output_name = getUOJProblemExtraOutputFileName($problem_conf, $problem_conf['n_ex_tests']);
UOJLocalRun::execAnd([
putUOJConf("$cur_dir/problem.conf", $problem_conf); ['cd', '/var/uoj_data'],
move_uploaded_file($input_file_name, "$cur_dir/$new_input_name"); ['zip', "{$this->id}.next.zip", $this->id, '-r', '-q'],
move_uploaded_file($output_file_name, "$cur_dir/$new_output_name"); ['mv', "{$this->id}.next.zip", "{$this->id}.zip", '-f'],
]);
if (dataSyncProblemData($problem) === '') {
rejudgeProblemAC($problem); return '';
}
public function sync() {
return $this->lock(LOCK_EX, fn () => $this->_sync());
}
}
function dataSyncProblemData($problem, $user = null) {
return (new SyncProblemDataHandler($problem, $user))->sync();
}
function dataAddHackPoint($problem, $uploaded_input_file, $uploaded_output_file, $reason = null, $user = null) {
if ($reason === null) {
if (UOJHack::cur()) {
$reason = [
'rejudge' => '自动重测本题所有获得 100 分的提交记录',
'hack_url' => HTML::url(UOJHack::cur()->getUri())
];
} else { } else {
error_log('hack successfully but sync failed.'); $reason = [];
} }
} }
?>
return (new SyncProblemDataHandler($problem, $user))->addHackPoint($uploaded_input_file, $uploaded_output_file, $reason);
}

View File

@ -0,0 +1,34 @@
<?php
class UOJProblemConfException extends Exception {
public function __construct($message) {
parent::__construct("<strong>problem.conf</strong> : $message");
}
}
class UOJFileNotFoundException extends Exception {
public function __construct($file_name) {
parent::__construct("file <strong>" . HTML::escape($file_name) . '</strong> not found');
}
}
class UOJSyncFailedException extends Exception {
public function __construct($msg) {
parent::__construct('同步失败:'.HTML::escape($msg));
}
}
class UOJUploadFailedException extends Exception {
public function __construct($msg) {
parent::__construct('上传失败:'.HTML::escape($msg));
}
}
class UOJInvalidArgumentException extends Exception {
public function __construct($msg) {
parent::__construct(HTML::escape($msg));
}
}
class UOJNotLoginException extends Exception {
public function __construct($msg = '未登录') {
parent::__construct(HTML::escape($msg));
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,186 +1,162 @@
<?php <?php
global $uojSupportedLanguages, $uojMainJudgerWorkPath;
$uojSupportedLanguages = array('C', 'C++', 'C++98', 'C++03', 'C++11', 'C++17', 'C++20', 'Pascal', 'Python2.7', 'Python3', 'Java8', 'Java11', 'Java17');
$uojMainJudgerWorkPath = "/opt/uoj/judger/uoj_judger";
function authenticateJudger() {
if (!is_string($_POST['judger_name']) || !is_string($_POST['password'])) {
return false;
}
$esc_judger_name = DB::escape($_POST['judger_name']);
$judger = DB::selectFirst("select password from judger_info where judger_name = '$esc_judger_name'");
if ($judger == null) {
return false;
}
return $judger['password'] == $_POST['password'];
}
function judgerCodeStr($code) {
switch ($code) {
case 0:
return "Accepted";
case 1:
return "Wrong Answer";
case 2:
return "Runtime Error";
case 3:
return "Memory Limit Exceeded";
case 4:
return "Time Limit Exceeded";
case 5:
return "Output Limit Exceeded";
case 6:
return "Dangerous Syscalls";
case 7:
return "Judgement Failed";
default:
return "No Comment";
}
}
class StrictFileReader {
private $f;
private $buf = '', $off = 0;
public function __construct($file_name) { function authenticateJudger() {
$this->f = fopen($file_name, 'r'); if (!is_string($_POST['judger_name']) || !is_string($_POST['password'])) {
} return false;
}
$judger = DB::selectFirst([
"select password from judger_info",
"where", [
"judger_name" => $_POST['judger_name'],
"enabled" => true,
"password" => $_POST['password']
]
]);
return $judger != null;
}
public function failed() { function judgerCodeStr($code) {
return $this->f === false; switch ($code) {
} case 0:
return "Accepted";
case 1:
return "Wrong Answer";
case 2:
return "Runtime Error";
case 3:
return "Memory Limit Exceeded";
case 4:
return "Time Limit Exceeded";
case 5:
return "Output Limit Exceeded";
case 6:
return "Dangerous Syscalls";
case 7:
return "Judgement Failed";
default:
return "No Comment";
}
}
public function readChar() { /**
if (isset($this->buf[$this->off])) { * Better to use UOJProblemConf instead
return $this->buf[$this->off++]; *
} * @return array|int
return fgetc($this->f); */
} function getUOJConf($file_name) {
public function unreadChar($c) { $ret = UOJProblemConf::getFromFile($file_name);
$this->buf .= $c; if ($ret instanceof UOJProblemConf) {
if ($this->off > 1000) { return $ret->conf;
$this->buf = substr($this->buf, $this->off); } else {
$this->off = 0; return $ret;
} }
} }
public function readString() { /**
$str = ''; * Better to use UOJProblemConf instead
while (true) { */
$c = $this->readChar(); function putUOJConf($file_name, $conf) {
if ($c === false) { (new UOJProblemConf($conf))->putToFile($file_name);
break; }
} elseif ($c === " " || $c === "\n" || $c === "\r") {
$this->unreadChar($c);
break;
} else {
$str .= $c;
}
}
return $str;
}
public function ignoreWhite() {
while (true) {
$c = $this->readChar();
if ($c === false) {
break;
} elseif ($c === " " || $c === "\n" || $c === "\r") {
continue;
} else {
$this->unreadChar($c);
break;
}
}
}
public function eof() { /**
return feof($this->f); * Better to use UOJProblemConf instead
} */
function getUOJConfVal($conf, $key, $default_val) {
return (new UOJProblemConf($conf))->getVal($key, $default_val);
}
public function close() { function getUOJProblemInputFileName($problem_conf, $num) {
fclose($this->f); return getUOJConfVal($problem_conf, 'input_pre', 'input') . $num . '.' . getUOJConfVal($problem_conf, 'input_suf', 'txt');
} }
} function getUOJProblemOutputFileName($problem_conf, $num) {
return getUOJConfVal($problem_conf, 'output_pre', 'output') . $num . '.' . getUOJConfVal($problem_conf, 'output_suf', 'txt');
}
function getUOJProblemExtraInputFileName($problem_conf, $num) {
return 'ex_' . getUOJConfVal($problem_conf, 'input_pre', 'input') . $num . '.' . getUOJConfVal($problem_conf, 'input_suf', 'txt');
}
function getUOJProblemExtraOutputFileName($problem_conf, $num) {
return 'ex_' . getUOJConfVal($problem_conf, 'output_pre', 'output') . $num . '.' . getUOJConfVal($problem_conf, 'output_suf', 'txt');
}
function getUOJConf($file_name) { function updateBestACSubmissions($username, $problem_id) {
$reader = new StrictFileReader($file_name); $update_best = function() use($username, $problem_id) {
if ($reader->failed()) { $best = DB::selectFirst([
return -1; "select id, used_time, used_memory, tot_size from submissions",
} "where", [
"submitter" => $username,
$conf = array(); "problem_id" => $problem_id,
while (!$reader->eof()) { "score" => 100
$reader->ignoreWhite(); ], "order by used_time, used_memory, tot_size asc limit 1", DB::for_share()
$key = $reader->readString(); ]);
if ($key === '') {
break;
}
$reader->ignoreWhite();
$val = $reader->readString();
if ($val === '') {
break;
}
if (isset($conf[$key])) {
return -2;
}
$conf[$key] = $val;
}
$reader->close();
return $conf;
}
function putUOJConf($file_name, $conf) {
$f = fopen($file_name, 'w');
foreach ($conf as $key => $val) {
fwrite($f, "$key $val\n");
}
fclose($f);
}
function getUOJConfVal($conf, $key, $default_val) {
if (isset($conf[$key])) {
return $conf[$key];
} else {
return $default_val;
}
}
function getUOJProblemInputFileName($problem_conf, $num) {
return getUOJConfVal($problem_conf, 'input_pre', 'input') . $num . '.' . getUOJConfVal($problem_conf, 'input_suf', 'txt');
}
function getUOJProblemOutputFileName($problem_conf, $num) {
return getUOJConfVal($problem_conf, 'output_pre', 'output') . $num . '.' . getUOJConfVal($problem_conf, 'output_suf', 'txt');
}
function getUOJProblemExtraInputFileName($problem_conf, $num) {
return 'ex_' . getUOJConfVal($problem_conf, 'input_pre', 'input') . $num . '.' . getUOJConfVal($problem_conf, 'input_suf', 'txt');
}
function getUOJProblemExtraOutputFileName($problem_conf, $num) {
return 'ex_' . getUOJConfVal($problem_conf, 'output_pre', 'output') . $num . '.' . getUOJConfVal($problem_conf, 'output_suf', 'txt');
}
function rejudgeProblem($problem) {
DB::query("update submissions set judge_time = NULL , result = '' , score = NULL , status = 'Waiting Rejudge' where problem_id = ${problem['id']}");
}
function rejudgeProblemAC($problem) {
DB::query("update submissions set judge_time = NULL , result = '' , score = NULL , status = 'Waiting Rejudge' where problem_id = ${problem['id']} and score = 100");
}
function rejudgeProblemGe97($problem) {
DB::query("update submissions set judge_time = NULL , result = '' , score = NULL , status = 'Waiting Rejudge' where problem_id = ${problem['id']} and score >= 97");
}
function rejudgeSubmission($submission) {
DB::query("update submissions set judge_time = NULL , result = '' , score = NULL , status = 'Waiting Rejudge' where id = ${submission['id']}");
}
function updateBestACSubmissions($username, $problem_id) {
$best = DB::selectFirst("select id, used_time, used_memory, tot_size from submissions where submitter = '$username' and problem_id = $problem_id and score = 100 order by used_time, used_memory, tot_size asc limit 1");
$shortest = DB::selectFirst("select id, used_time, used_memory, tot_size from submissions where submitter = '$username' and problem_id = $problem_id and score = 100 order by tot_size, used_time, used_memory asc limit 1");
DB::delete("delete from best_ac_submissions where submitter = '$username' and problem_id = $problem_id");
if ($best) { if ($best) {
DB::insert("insert into best_ac_submissions (problem_id, submitter, submission_id, used_time, used_memory, tot_size, shortest_id, shortest_used_time, shortest_used_memory, shortest_tot_size) values ($problem_id, '$username', ${best['id']}, ${best['used_time']}, ${best['used_memory']}, ${best['tot_size']}, ${shortest['id']}, ${shortest['used_time']}, ${shortest['used_memory']}, ${shortest['tot_size']})"); $shortest = DB::selectFirst([
} "select id, used_time, used_memory, tot_size from submissions",
"where", [
"submitter" => $username,
"problem_id" => $problem_id,
"score" => 100
], "order by tot_size, used_time, used_memory asc limit 1", DB::for_share()
]);
$keys = [
'submission_id', 'used_time', 'used_memory', 'tot_size',
'shortest_id', 'shortest_used_time', 'shortest_used_memory', 'shortest_tot_size'
];
$vals = [
$best['id'], $best['used_time'], $best['used_memory'], $best['tot_size'],
$shortest['id'], $shortest['used_time'], $shortest['used_memory'], $shortest['tot_size']
];
$fields_str = '(problem_id, submitter';
for ($i = 0; $i < count($keys); $i++) {
$fields_str .= ", {$keys[$i]}";
}
$fields_str .= ')';
$cnt = DB::selectCount("select count(*) from best_ac_submissions where submitter='$username'"); DB::insert([
DB::update("update user_info set ac_num = $cnt where username='$username'"); "insert into best_ac_submissions",
$fields_str,
DB::update("update problems set ac_num = (select count(*) from submissions where problem_id = problems.id and score = 100), submit_num = (select count(*) from submissions where problem_id = problems.id) where id = $problem_id"); "values", DB::tuple(array_merge([$problem_id, $username], $vals)),
} "on duplicate key update", array_combine($keys, $vals)
?> ]);
} else {
DB::delete([
"delete from best_ac_submissions",
"where", [
"submitter" => $username,
"problem_id" => $problem_id
]
]);
}
};
DB::transaction($update_best);
DB::update([
"update user_info",
"set", [
"ac_num" => DB::rawbracket([
"select count(*) from best_ac_submissions",
"where", ["submitter" => $username]
])
], "where", ["username" => $username]
]);
DB::update([
"update problems",
"set", [
"ac_num" => DB::rawbracket([
"select count(*) from submissions",
"where", [
"problem_id" => DB::raw("problems.id"),
"score" => 100
]
]),
"submit_num" => DB::rawbracket([
"select count(*) from submissions",
"where", [
"problem_id" => DB::raw("problems.id")
]
])
], "where", ["id" => $problem_id]
]);
}

View File

@ -1,36 +1,35 @@
<?php <?php
error_reporting(E_ALL ^ E_NOTICE); error_reporting(E_ALL ^ E_NOTICE);
spl_autoload_register(function($class_name) { spl_autoload_register(function ($class_name) {
$lib = $_SERVER['DOCUMENT_ROOT'] . '/app/models/' . $class_name . '.php'; $lib = $_SERVER['DOCUMENT_ROOT'] . '/app/models/' . $class_name . '.php';
if (file_exists($lib)) { if (file_exists($lib)) {
require_once $lib; require_once $lib;
}
});
function requireLib($name) { // html lib
global $REQUIRE_LIB;
$REQUIRE_LIB[$name] = '';
} }
function requirePHPLib($name) { // uoj php lib });
require $_SERVER['DOCUMENT_ROOT'].'/app/libs/uoj-'.$name.'-lib.php';
} function requireLib($name) { // html lib
global $REQUIRE_LIB;
requirePHPLib('validate'); $REQUIRE_LIB[$name] = '';
requirePHPLib('query'); }
requirePHPLib('rand'); function requirePHPLib($name) { // uoj php lib
requirePHPLib('utility'); require $_SERVER['DOCUMENT_ROOT'] . '/app/libs/uoj-' . $name . '-lib.php';
requirePHPLib('security'); }
requirePHPLib('contest');
requirePHPLib('html'); requirePHPLib('expection');
requirePHPLib('validate');
Session::init(); requirePHPLib('query');
UOJTime::init(); requirePHPLib('rand');
DB::init(); requirePHPLib('utility');
Auth::init(); requirePHPLib('security');
requirePHPLib('contest');
if (isset($_GET['locale'])) { requirePHPLib('html');
UOJLocale::setLocale($_GET['locale']);
} Session::init();
UOJLocale::requireModule('basic'); UOJTime::init();
?> DB::init();
$myUser = null;
Auth::init();
UOJLocale::init();

View File

@ -12,31 +12,6 @@ function hasProblemPermission($user, $problem) {
} }
return DB::selectFirst("select * from problems_permissions where username = '{$user['username']}' and problem_id = {$problem['id']}") != null; return DB::selectFirst("select * from problems_permissions where username = '{$user['username']}' and problem_id = {$problem['id']}") != null;
} }
function hasViewPermission($str, $user, $problem, $submission) {
if ($str == 'ALL') {
return true;
}
if ($str == 'ALL_AFTER_AC') {
return hasAC($user,$problem);
}
if ($str == 'SELF') {
return $submission['submitter'] == $user['username'];
}
return false;
}
function hasViewSolutionPermission($str, $user, $problem) {
if (isSuperUser($user) || isProblemManager($user)) {
return true;
}
if ($str == 'ALL') {
return true;
}
if ($str == 'ALL_AFTER_AC') {
return hasAC($user, $problem);
}
return false;
}
function hasContestPermission($user, $contest) { function hasContestPermission($user, $contest) {
if ($user == null) { if ($user == null) {
@ -51,14 +26,6 @@ function hasContestPermission($user, $contest) {
function hasRegistered($user, $contest) { function hasRegistered($user, $contest) {
return DB::selectFirst("select * from contests_registrants where username = '${user['username']}' and contest_id = ${contest['id']}") != null; return DB::selectFirst("select * from contests_registrants where username = '${user['username']}' and contest_id = ${contest['id']}") != null;
} }
function hasAC($user, $problem) {
return DB::selectFirst("select * from best_ac_submissions where submitter = '${user['username']}' and problem_id = ${problem['id']}") != null;
}
function hasParticipated($user, $contest) {
$result = DB::selectFirst("select * from contests_registrants where username = '${user['username']}' and contest_id = ${contest['id']}");
return $result != null && $result['has_participated'];
}
function queryUser($username) { function queryUser($username) {
if (!validateUsername($username)) { if (!validateUsername($username)) {
@ -66,9 +33,6 @@ function queryUser($username) {
} }
return DB::selectFirst("select * from user_info where username='$username'", MYSQLI_ASSOC); return DB::selectFirst("select * from user_info where username='$username'", MYSQLI_ASSOC);
} }
function queryProblemContent($id) {
return DB::selectFirst("select * from problems_contents where id = $id", MYSQLI_ASSOC);
}
function queryProblemBrief($id) { function queryProblemBrief($id) {
return DB::selectFirst("select * from problems where id = $id", MYSQLI_ASSOC); return DB::selectFirst("select * from problems where id = $id", MYSQLI_ASSOC);
} }
@ -108,27 +72,15 @@ function queryContestProblemRank($contest, $problem) {
if (!DB::selectFirst("select * from contests_problems where contest_id = {$contest['id']} and problem_id = {$problem['id']}")) { if (!DB::selectFirst("select * from contests_problems where contest_id = {$contest['id']} and problem_id = {$problem['id']}")) {
return null; return null;
} }
$contest_problems = DB::selectAll("select problem_id from contests_problems where contest_id = {$contest['id']} order by dfn, problem_id"); $contest_problems = DB::selectAll("select problem_id from contests_problems where contest_id = {$contest['id']} order by level, problem_id");
return array_search(array('problem_id' => $problem['id']), $contest_problems) + 1; return array_search(array('problem_id' => $problem['id']), $contest_problems) + 1;
} }
function querySubmission($id) {
return DB::selectFirst("select * from submissions where id = $id", MYSQLI_ASSOC);
}
function queryHack($id) {
return DB::selectFirst("select * from hacks where id = $id", MYSQLI_ASSOC);
}
function queryContest($id) { function queryContest($id) {
return DB::selectFirst("select * from contests where id = $id", MYSQLI_ASSOC); return DB::selectFirst("select * from contests where id = $id", MYSQLI_ASSOC);
} }
function queryContestProblem($id) {
return DB::selectFirst("select * from contest_problems where contest_id = $id", MYSQLI_ASSOC);
}
function queryContestProblems($id) {
return DB::selectAll("select * from contests_problems where contest_id = $id order by dfn, problem_id", MYSQLI_ASSOC);
}
function queryGroup($id) { function queryGroup($id) {
return DB::selectFirst("select * from groups where id = $id", MYSQLI_ASSOC); return DB::selectFirst("select * from `groups` where id = $id", MYSQLI_ASSOC);
} }
function queryGroupUsers($id) { function queryGroupUsers($id) {
return DB::selectAll("SELECT * FROM groups_users WHERE group_id = $id"); return DB::selectAll("SELECT * FROM groups_users WHERE group_id = $id");
@ -137,10 +89,23 @@ function queryUserInGroup($group_id, $username) {
return DB::selectFirst("select * from groups_users where username='$username' and group_id='$group_id'", MYSQLI_ASSOC); return DB::selectFirst("select * from groups_users where username='$username' and group_id='$group_id'", MYSQLI_ASSOC);
} }
function queryGroupsOfUser($username) { function queryGroupsOfUser($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); return DB::selectAll([
"select", DB::fields([
"title" => "groups.title",
"id" => "groups.id",
]),
"from groups_users",
"inner join `groups`", "on", [
"groups_users.group_id" => DB::raw("groups.id"),
],
"where", [
"groups_users.username" => $username,
"groups.is_hidden" => false,
],
]);
} }
function queryGroupmateCurrentAC($username) { 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); 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) { 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); 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);
@ -154,30 +119,9 @@ function queryGroupActiveAssignments($group_id) {
function queryAssignmentByGroupListID($group_id, $list_id) { function queryAssignmentByGroupListID($group_id, $list_id) {
return DB::selectFirst("select * from groups_assignments where list_id='$list_id' and group_id='$group_id'", MYSQLI_ASSOC); return DB::selectFirst("select * from groups_assignments where list_id='$list_id' and group_id='$group_id'", MYSQLI_ASSOC);
} }
function queryZanVal($id, $type, $user) {
if ($user == null) {
return 0;
}
$esc_type = DB::escape($type);
$row = DB::selectFirst("select val from click_zans where username='{$user['username']}' and type='$esc_type' and target_id='$id'");
if ($row == null) {
return 0;
}
return $row['val'];
}
function queryBlog($id) { function queryBlog($id) {
return DB::selectFirst("select * from blogs where id='$id'", MYSQLI_ASSOC); return DB::selectFirst("select * from blogs where id='$id'", MYSQLI_ASSOC);
} }
function queryBlogTags($id) {
$tags = array();
$result = DB::select("select tag from blogs_tags where blog_id = $id order by id");
while ($row = DB::fetch($result, MYSQLI_NUM)) {
$tags[] = $row[0];
}
return $tags;
}
function queryBlogComment($id) { function queryBlogComment($id) {
return DB::selectFirst("select * from blogs_comments where id='$id'", MYSQLI_ASSOC); return DB::selectFirst("select * from blogs_comments where id='$id'", MYSQLI_ASSOC);
} }
@ -188,92 +132,21 @@ function isProblemVisibleToUser($problem, $user) {
function isListVisibleToUser($list, $user) { function isListVisibleToUser($list, $user) {
return !$list['is_hidden'] || isSuperUser($user); return !$list['is_hidden'] || isSuperUser($user);
} }
function isContestProblemVisibleToUser($problem, $contest, $user) {
if (isProblemVisibleToUser($problem, $user)) {
return true;
}
if ($contest['cur_progress'] >= CONTEST_PENDING_FINAL_TEST) {
return true;
}
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {
return false;
}
return hasRegistered($user, $contest);
}
function isSubmissionVisibleToUser($submission, $problem, $user) {
if (isProblemManager($user)) {
return true;
} elseif (!$submission['is_hidden']) {
return true;
} else {
return hasProblemPermission($user, $problem);
}
}
function isHackVisibleToUser($hack, $problem, $user) {
if (isSuperUser($user)) {
return true;
} elseif (!$hack['is_hidden']) {
return true;
} else {
return hasProblemPermission($user, $problem);
}
}
function isSubmissionFullVisibleToUser($submission, $contest, $problem, $user) {
if (isSuperUser($user)) {
return true;
} elseif ($submission['submitter'] == $user['username']) {
return true;
} elseif (isRegisteredRunningContestProblem($user, $problem)) {
return false;
} elseif (!$contest) {
return true;
} elseif ($contest['cur_progress'] > CONTEST_IN_PROGRESS) {
return true;
} else {
return hasProblemPermission($user, $problem);
}
}
function isHackFullVisibleToUser($hack, $contest, $problem, $user) {
if (isSuperUser($user)) {
return true;
} elseif (!$contest) {
return true;
} elseif ($contest['cur_progress'] > CONTEST_IN_PROGRESS) {
return true;
} elseif ($hack['hacker'] == $user['username']) {
return true;
} else {
return hasProblemPermission($user, $problem);
}
}
function isRegisteredRunningContestProblem($user, $problem) { function isRegisteredRunningContestProblem($user, $problem) {
$result = DB::query("select contest_id from contests_problems where problem_id = {$problem['id']}"); $result = DB::query("select contest_id from contests_problems where problem_id = {$problem['id']}");
while (list($contest_id) = DB::fetch($result, MYSQLI_NUM)) { while (list($contest_id) = DB::fetch($result, MYSQLI_NUM)) {
$contest = queryContest($contest_id); $contest = queryContest($contest_id);
genMoreContestInfo($contest); genMoreContestInfo($contest);
if ($contest['cur_progress'] == CONTEST_IN_PROGRESS if (
$contest['cur_progress'] == CONTEST_IN_PROGRESS
&& hasRegistered($user, $contest) && hasRegistered($user, $contest)
&& !hasContestPermission($user, $contest) && !hasContestPermission($user, $contest)
&& queryContestProblemRank($contest, $problem)) { && queryContestProblemRank($contest, $problem)
) {
return true; return true;
} }
} }
return false; return false;
} }
function deleteBlog($id) {
if (!validateUInt($id)) {
return;
}
DB::delete("delete from click_zans where type = 'B' and target_id = $id");
DB::delete("delete from click_zans where type = 'BC' and target_id in (select id from blogs_comments where blog_id = $id)");
DB::delete("delete from blogs where id = $id");
DB::delete("delete from blogs_comments where blog_id = $id");
DB::delete("delete from important_blogs where blog_id = $id");
DB::delete("delete from blogs_tags where blog_id = $id");
DB::delete("DELETE FROM problems_solutions WHERE blog_id = $id");
}

View File

@ -16,8 +16,8 @@ function uojRandString($len, $charset = '0123456789abcdefghijklmnopqrstuvwxyz')
function uojRandAvaiableFileName($dir, $length = 20, $suffix = '') { function uojRandAvaiableFileName($dir, $length = 20, $suffix = '') {
do { do {
$fileName = $dir . uojRandString($length); $fileName = $dir . uojRandString($length);
} while (file_exists(UOJContext::storagePath().$fileName.$suffix)); } while (file_exists(UOJContext::storagePath() . $fileName . $suffix));
return $fileName.$suffix; return $fileName . $suffix;
} }
function uojRandAvaiableTmpFileName() { function uojRandAvaiableTmpFileName() {
@ -26,8 +26,8 @@ function uojRandAvaiableTmpFileName() {
function uojRandAvaiableSubmissionFileName() { function uojRandAvaiableSubmissionFileName() {
$num = uojRand(1, 10000); $num = uojRand(1, 10000);
if (!file_exists(UOJContext::storagePath()."/submission/$num")) { if (!file_exists(UOJContext::storagePath() . "/submission/$num")) {
mkdir(UOJContext::storagePath()."/submission/$num", 0777, true); mkdir(UOJContext::storagePath() . "/submission/$num", 0777, true);
} }
return uojRandAvaiableFileName("/submission/$num/"); return uojRandAvaiableFileName("/submission/$num/");
} }

View File

@ -28,6 +28,68 @@ function crsf_check() {
} }
function crsf_defend() { function crsf_defend() {
if (!crsf_check()) { if (!crsf_check()) {
becomeMsgPage('This page has expired.'); UOJResponse::page403('页面已过期(可能页面真的过期了,也可能是刚才你访问的网页没有完全加载,也可能是你的浏览器版本太老)');
} }
} }
function submission_frequency_check() {
$recent = clone UOJTime::$time_now;
$recent->sub(new DateInterval("PT1S"));
$num = DB::selectCount([
"select count(*) from submissions",
"where", [
"submitter" => Auth::id(),
["submit_time", ">=", $recent->format('Y-m-d H:i:s')]
]
]);
if ($num >= 1) {
return false;
}
// use the implementation below if OJ is under attack
/*
// 1
$recent = clone UOJTime::$time_now;
$recent->sub(new DateInterval("PT3S"));
$num = DB::selectCount([
"select count(*) from submissions",
"where", [
"submitter" => Auth::id(),
["submit_time", ">=", $recent->format('Y-m-d H:i:s')]
]
]);
if ($num >= 1) {
return false;
}
// 2
$recent = clone UOJTime::$time_now;
$recent->sub(new DateInterval("PT1M"));
$num = DB::selectCount([
"select count(*) from submissions",
"where", [
"submitter" => Auth::id(),
["submit_time", ">=", $recent->format('Y-m-d H:i:s')]
]
]);
if ($num >= 6) {
return false;
}
// 3
$recent = clone UOJTime::$time_now;
$recent->sub(new DateInterval("PT30M"));
$num = DB::selectCount([
"select count(*) from submissions",
"where", [
"submitter" => Auth::id(),
["submit_time", ">=", $recent->format('Y-m-d H:i:s')]
]
]);
if ($num >= 30) {
return false;
}
*/
return true;
}

View File

@ -10,6 +10,18 @@ function mergeConfig(&$config, $default_config) {
} }
} }
function is_assoc($arr) {
if (!is_array($arr)) {
return false;
}
foreach (array_keys($arr) as $key) {
if (!is_int($key)) {
return true;
}
}
return false;
}
function strStartWith($str, $pre) { function strStartWith($str, $pre) {
return substr($str, 0, strlen($pre)) === $pre; return substr($str, 0, strlen($pre)) === $pre;
} }
@ -31,7 +43,7 @@ function uojTextEncode($str, $config = array()) {
'allow_CR' => false, 'allow_CR' => false,
'html_escape' => false 'html_escape' => false
]); ]);
$allow = array(); $allow = array();
for ($c = 32; $c <= 126; $c++) { for ($c = 32; $c <= 126; $c++) {
$allow[chr($c)] = true; $allow[chr($c)] = true;
@ -39,11 +51,11 @@ function uojTextEncode($str, $config = array()) {
$allow["\n"] = true; $allow["\n"] = true;
$allow[" "] = true; $allow[" "] = true;
$allow["\t"] = true; $allow["\t"] = true;
if ($config['allow_CR']) { if ($config['allow_CR']) {
$allow["\r"] = true; $allow["\r"] = true;
} }
$len = strlen($str); $len = strlen($str);
$ok = true; $ok = true;
for ($i = 0; $i < $len; $i++) { for ($i = 0; $i < $len; $i++) {
@ -129,6 +141,15 @@ function blog_name_decode($name) {
return $name; return $name;
} }
function camelize($str, $delimiters = '-_') {
$str = ucwords($str, $delimiters);
foreach (str_split($delimiters) as $c) {
$str = str_replace($c, '', $str);
}
return $str;
}
function addUserType(&$user, $type) { function addUserType(&$user, $type) {
$usertype = explode(',', $user['usertype']); $usertype = explode(',', $user['usertype']);
if (!in_array($type, $usertype)) { if (!in_array($type, $usertype)) {
@ -150,9 +171,6 @@ function hasUserType($user, $type) {
return in_array($type, $usertype); return in_array($type, $usertype);
} }
function isNormalUser($user) {
return $user != null && !hasUserType($user, 'contest_only');
}
function isProblemUploader($user) { function isProblemUploader($user) {
if ($user == null) { if ($user == null) {
return false; return false;
@ -178,9 +196,12 @@ function isContestJudger($user) {
function isSuperUser($user) { function isSuperUser($user) {
return $user != null && $user['usergroup'] == 'S'; return $user != null && $user['usergroup'] == 'S';
} }
function isTmpUser($user) {
return $user != null && $user['usergroup'] == 'T';
}
function getProblemExtraConfig($problem) { function getProblemExtraConfig($problem) {
$extra_config = json_decode($problem['extra_config'], true); $extra_config = json_decode($problem['extra_config'], true);
$default_extra_config = array( $default_extra_config = array(
'view_content_type' => 'ALL', 'view_content_type' => 'ALL',
'view_all_details_type' => 'ALL', 'view_all_details_type' => 'ALL',
@ -188,9 +209,9 @@ function getProblemExtraConfig($problem) {
'view_solution_type' => 'ALL', 'view_solution_type' => 'ALL',
'submit_solution_type' => 'ALL_AFTER_AC', 'submit_solution_type' => 'ALL_AFTER_AC',
); );
mergeConfig($extra_config, $default_extra_config); mergeConfig($extra_config, $default_extra_config);
return $extra_config; return $extra_config;
} }
function getProblemSubmissionRequirement($problem) { function getProblemSubmissionRequirement($problem) {
@ -223,7 +244,20 @@ function getProblemCustomTestRequirement($problem) {
} }
function sendSystemMsg($username, $title, $content) { function sendSystemMsg($username, $title, $content) {
$content = DB::escape($content); DB::insert([
$title = DB::escape($title); "insert into user_system_msg",
DB::insert("insert into user_system_msg (receiver, title, content, send_time) values ('$username', '$title', '$content', now())"); "(receiver, title, content, send_time)",
"values", DB::tuple([$username, $title, $content, DB::now()])
]);
}
function retry_loop(callable $f, $retry = 5, $ms = 10) {
for ($i = 0; $i < $retry; $i++) {
$ret = $f();
if ($ret !== false) {
return $ret;
}
usleep($ms * 1000);
}
return $ret;
} }

View File

@ -59,3 +59,19 @@ function validateString($str) {
function validateGitHubUsername($username) { function validateGitHubUsername($username) {
return is_string($username) && preg_match('/^[a-zA-Z0-9_-]{1,20}$/', $username); return is_string($username) && preg_match('/^[a-zA-Z0-9_-]{1,20}$/', $username);
} }
function validateUserAndStoreByUsername($username, &$vdata) {
if (!isset($vdata['user'])) {
$vdata['user'] = [];
}
$user = UOJUser::query($username);
if (!$user) {
return "不存在名为{$username}的用户";
}
$vdata['user'][$username] = $user;
return '';
}
function is_short_string($str) {
return is_string($str) && strlen($str) <= 256;
}

View File

@ -14,6 +14,7 @@ return [
'in progress' => 'in progress', 'in progress' => 'in progress',
'pending final test' => 'pending final test', 'pending final test' => 'pending final test',
'final testing' => 'final testing', 'final testing' => 'final testing',
'official results to be announced' => 'official results to be announced',
'ended' => 'ended', 'ended' => 'ended',
'total score' => 'total score', 'total score' => 'total score',
'add new contest' => 'Add new contest', 'add new contest' => 'Add new contest',
@ -23,10 +24,11 @@ return [
'after contest standings' => 'After contest standings', 'after contest standings' => 'After contest standings',
'contest backstage' => 'Backstage', 'contest backstage' => 'Backstage',
'contest notice' => 'Notice', 'contest notice' => 'Notice',
'show all submissions' => 'Show all submissions', 'show all submissions' => "Show everyone's submissions",
'contest ends in' => 'Contest ends in', 'contest ends in' => 'Contest ends in',
'contest pending final test' => 'Pending final test', 'contest pending final test' => 'Pending final test',
'contest final testing' => 'Final testing', 'contest final testing' => 'Final testing',
'contest official results to be announced' => 'Official results to be announced',
'contest ended' => 'Contest Ended', 'contest ended' => 'Contest Ended',
'contest registrants' => 'Registrants', 'contest registrants' => 'Registrants',
'problem self review' => 'Problem self review', 'problem self review' => 'Problem self review',

View File

@ -14,6 +14,7 @@ return [
'in progress' => '正在进行', 'in progress' => '正在进行',
'pending final test' => '等待评测', 'pending final test' => '等待评测',
'final testing' => '正在评测', 'final testing' => '正在评测',
'official results to be announced' => '等待宣布正式成绩',
'ended' => '已结束', 'ended' => '已结束',
'total score' => '总分', 'total score' => '总分',
'add new contest' => '添加比赛', 'add new contest' => '添加比赛',
@ -23,10 +24,11 @@ return [
'after contest standings' => '赛后排行榜', 'after contest standings' => '赛后排行榜',
'contest backstage' => '大后台', 'contest backstage' => '大后台',
'contest notice' => '比赛通知', 'contest notice' => '比赛通知',
'show all submissions' => '显示所有提交', 'show all submissions' => '显示所有人的提交',
'contest ends in' => '距离比赛结束', 'contest ends in' => '距离比赛结束',
'contest pending final test' => '等待评测', 'contest pending final test' => '等待评测',
'contest final testing' => '正在测评', 'contest final testing' => '正在测评',
'contest official results to be announced' => '等待宣布正式成绩',
'contest ended' => '比赛已结束', 'contest ended' => '比赛已结束',
'contest registrants' => '报名选手列表', 'contest registrants' => '报名选手列表',
'problem self review' => '题目总结', 'problem self review' => '题目总结',

View File

@ -3,26 +3,43 @@
class Auth { class Auth {
public static function check() { public static function check() {
global $myUser; global $myUser;
return $myUser != null; return $myUser !== null;
} }
public static function id() { public static function id() {
global $myUser; global $myUser;
if ($myUser === null) {
return null;
}
return $myUser['username']; return $myUser['username'];
} }
public static function user() { public static function user() {
global $myUser; global $myUser;
return $myUser; return $myUser;
} }
public static function property($name) {
global $myUser;
if (!$myUser) {
return false;
}
return $myUser[$name];
}
public static function login($username, $remember = true) { public static function login($username, $remember = true) {
if (!validateUsername($username)) { if (!validateUsername($username)) {
return; return;
} }
$_SESSION['username'] = $username; $_SESSION['username'] = $username;
if ($remember) { if ($remember) {
$remember_token = DB::selectFirst("select remember_token from user_info where username = '$username'")['remember_token']; $remember_token = DB::selectSingle([
"select remember_token from user_info",
"where", ["username" => $username]
]);
if ($remember_token == '') { if ($remember_token == '') {
$remember_token = uojRandString(60); $remember_token = uojRandString(60);
DB::update("update user_info set remember_token = '$remember_token', last_login = now() where username = '$username'"); DB::update([
"update user_info",
"set", ["remember_token" => $remember_token],
"where", ["username" => $username]
]);
} }
$_SESSION['last_login'] = time(); $_SESSION['last_login'] = time();
@ -30,6 +47,12 @@ class Auth {
Cookie::safeSet('uoj_username', $username, $expire, '/', array('httponly' => true)); Cookie::safeSet('uoj_username', $username, $expire, '/', array('httponly' => true));
Cookie::safeSet('uoj_remember_token', $remember_token, $expire, '/', array('httponly' => true)); Cookie::safeSet('uoj_remember_token', $remember_token, $expire, '/', array('httponly' => true));
} }
DB::update([
"update user_info",
"set", ["last_login_time" => UOJTime::$time_now_str],
"where", ["username" => $username]
]);
} }
public static function logout() { public static function logout() {
unset($_SESSION['username']); unset($_SESSION['username']);
@ -37,7 +60,11 @@ class Auth {
unset($_SESSION['last_visited']); unset($_SESSION['last_visited']);
Cookie::safeUnset('uoj_username', '/'); Cookie::safeUnset('uoj_username', '/');
Cookie::safeUnset('uoj_remember_token', '/'); Cookie::safeUnset('uoj_remember_token', '/');
DB::update("update user_info set remember_token = '' where username = '".Auth::id()."'"); DB::update([
"update user_info",
"set", ["remember_token" => ''],
"where", ["username" => Auth::id()]
]);
} }
private static function initMyUser() { private static function initMyUser() {
@ -51,7 +78,7 @@ class Auth {
if (!validateUsername($_SESSION['username'])) { if (!validateUsername($_SESSION['username'])) {
return; return;
} }
$myUser = queryUser($_SESSION['username']); $myUser = UOJUser::query($_SESSION['username']);
return; return;
} }
@ -61,7 +88,7 @@ class Auth {
if (!validateUsername($username)) { if (!validateUsername($username)) {
return; return;
} }
$myUser = queryUser($username); $myUser = UOJUser::query($username);
if ($myUser['remember_token'] !== $remember_token) { if ($myUser['remember_token'] !== $remember_token) {
$myUser = null; $myUser = null;
} }
@ -72,24 +99,19 @@ class Auth {
global $myUser; global $myUser;
Auth::initMyUser(); Auth::initMyUser();
if ($myUser && UOJUser::getAccountStatus($myUser) != 'ok') {
if ($myUser) { $myUser = null;
if ($myUser['usergroup'] == 'B') {
$myUser = null;
}
} }
if ($myUser) { if ($myUser) {
if (!isset($_SESSION['last_login'])) { if (!isset($_SESSION['last_login'])) {
$_SESSION['last_login'] = strtotime($myUser['last_login']); $_SESSION['last_login'] = strtotime($myUser['last_login_time']);
} }
if ((time() - $_SESSION['last_login']) > 60 * 60 * 24 * 7) { // 1 week $myUser = UOJUser::updateVisitHistory($myUser, [
Auth::logout(); 'remote_addr' => UOJContext::remoteAddr(),
$myUser = null; 'http_x_forwarded_for' => UOJContext::httpXForwardedFor(),
} 'http_user_agent' => UOJContext::httpUserAgent()
]);
$_SESSION["last_visited"] = time(); $_SESSION['last_visited'] = time();
DB::update("update user_info set remote_addr = '".DB::escape($_SERVER['REMOTE_ADDR'])."', http_x_forwarded_for = '".DB::escape($_SERVER['HTTP_X_FORWARDED_FOR'])."', last_visited = now() where username = '".DB::escape($myUser['username'])."'");
} }
} }
} }

View File

@ -0,0 +1,121 @@
<?php
class ClickZans {
public static function getTable($type) {
switch ($type) {
case 'B':
return 'blogs';
case 'BC':
return 'blogs_comments';
case 'P':
return 'problems';
case 'C':
return 'contests';
}
return null;
}
public static function canClickZan($id, $type, $user) {
switch ($type) {
case 'P':
return UOJProblem::query($id)->userCanClickZan($user);
default:
return true;
}
}
public static function query($id, $type, $user) {
if ($user == null) {
return 0;
}
$row = DB::selectFirst([
"select val from click_zans",
"where", [
'username' => $user['username'],
'type' => $type,
'target_id' => $id
]
]);
if ($row == null) {
return 0;
}
return $row['val'];
}
public static function click($id, $type, $user, $delta, $show_text = true) {
if (!DB::$in_transaction) {
return DB::transaction(fn() => ClickZans::click($id, $type, $user, $delta));
}
$table_name = ClickZans::getTable($type);
$cur = ClickZans::query($id, $type, $user);
$row = DB::selectFirst([
"select zan from", DB::table($table_name),
"where", ['id' => $id], DB::for_update()
]);
if (!$row) {
return '<div class="text-danger">failed</div>';
}
if ($cur != $delta) {
$cur += $delta;
if ($cur == 0) {
DB::delete([
"delete from click_zans",
"where", [
'username' => Auth::id(),
'type' => $type,
'target_id' => $id
]
]);
} elseif ($cur != $delta) {
DB::update([
"update click_zans",
"set", ['val' => $cur],
"where", [
'username' => Auth::id(),
'type' => $type,
'target_id' => $id
]
]);
} else {
DB::insert([
"insert into click_zans",
"(username, type, target_id, val)", "values",
DB::tuple([Auth::id(), $type, $id, $cur])
]);
}
$cnt = $row['zan'] + $delta;
DB::update([
"update", DB::table($table_name),
"set", ['zan' => $cnt],
"where", ['id' => $id]
]);
} else {
$cnt = $row['zan'];
}
return ClickZans::getBlock($type, $id, $cnt, $cur, $show_text);
}
public static function getBlock($type, $id, $cnt, $val = null, $show_text = true) {
if ($val === null) {
$val = ClickZans::query($id, $type, Auth::user());
}
return '<div class="uoj-click-zan-block" data-id="'.$id.'" data-type="'.$type.'" data-val="'.$val.'" data-cnt="'.$cnt.'" data-show-text="'.$show_text.'"></div>';
}
public static function getCntBlock($cnt) {
$cls = 'uoj-click-zan-block-';
if ($cnt > 0) {
$cls .= 'positive';
} elseif ($cnt < 0) {
$cls .= 'negative';
} else {
$cls .= 'neutral';
}
return '<span class="'.$cls.'"><span class="uoj-click-zan-cnt">[<strong>' . ($cnt > 0 ? '+' . $cnt : $cnt) . '</strong>]</span></span>';
}
}

View File

@ -1,78 +1,451 @@
<?php <?php
class DB { class DB {
public static mysqli $conn;
public static array $cache = [];
public static bool $in_transaction = false;
const WLOCK = "WRITE";
const RLOCK = "READ";
const ASSOC = MYSQLI_ASSOC;
const NUM = MYSQLI_NUM;
const BOTH = MYSQLI_BOTH;
public static function init() { public static function init() {
global $uojMySQL; $server = UOJConfig::$data['database']['host'];
@$uojMySQL = mysqli_connect(UOJConfig::$data['database']['host'] . ':' . UOJConfig::$data['database']['port'], UOJConfig::$data['database']['username'], UOJConfig::$data['database']['password'], UOJConfig::$data['database']['database']); $username = UOJConfig::$data['database']['username'];
if (!$uojMySQL) { $password = UOJConfig::$data['database']['password'];
echo 'There is something wrong with database >_<.... ' . mysqli_connect_error(); $dbname = UOJConfig::$data['database']['database'];
die(); DB::$conn = new mysqli($server, $username, $password, $dbname);
if (DB::$conn->connect_error) {
UOJLog::error('database initialization failed: '. DB::$conn->connect_error);
die('There is something wrong with the database >_<... Connection failed');
}
if (!DB::$conn->set_charset("utf8mb4")) {
UOJLog::error("database initialization failed: Error loading character set utf8. " . DB::$conn->error);
die('There is something wrong with the database >_<.... Charset utf8 not supported');
} }
} }
public static function escape($str) {
global $uojMySQL; // lc: local cache
return mysqli_real_escape_string($uojMySQL, $str); public static function lc() {
return new DBUseLocalCache('');
} }
public static function fetch($r, $opt = MYSQLI_ASSOC) {
global $uojMySQL; public static function escape($str) {
return mysqli_fetch_array($r, $opt); return DB::$conn->real_escape_string($str);
}
public static function raw($str) {
return new DBRawString($str);
}
public static function rawbracket($q) {
return DB::raw(DB::bracket($q));
}
public static function rawvalue($str) {
return DB::raw(DB::value($str));
}
public static function rawtuple(array $vals) {
return DB::raw(DB::tuple($vals));
}
public static function call($fun, ...$args) {
return DB::raw("{$fun}(".implode(',', array_map('DB::value', $args)).')');
}
public static function now() {
return DB::call('now');
}
public static function instr($str, $substr) {
return DB::call('instr', $str, $substr);
}
public static function cast_as_json($value) {
return DB::raw('cast('.DB::value($value).' as json)');
}
public static function json_set($json_doc, ...$args) {
return DB::call('json_set', DB::raw($json_doc), ...$args);
}
public static function json_insert($json_doc, ...$args) {
return DB::call('json_insert', DB::raw($json_doc), ...$args);
}
public static function json_replace($json_doc, ...$args) {
return DB::call('json_replace', DB::raw($json_doc), ...$args);
}
public static function json_remove($json_doc, ...$args) {
return DB::call('json_remove', DB::raw($json_doc), ...$args);
}
public static function json_array_append($json_doc, ...$args) {
return DB::call('json_array_append', DB::raw($json_doc), ...$args);
}
public static function json_array_insert($json_doc, ...$args) {
return DB::call('json_array_insert', DB::raw($json_doc), ...$args);
}
public static function json_unquote($json_doc) {
return DB::call('json_unquote', DB::raw($json_doc));
}
public static function table($table) {
//return '`'.str_replace('`', '``', $table).'`';
return $table;
}
public static function fields($fields) {
if (is_assoc($fields)) {
$new_fields = [];
foreach ($fields as $name => $val) {
if (is_int($name)) {
$new_fields[] = $val;
} else {
$new_fields[] = DB::field_as($val, $name);
}
}
$fields = $new_fields;
}
return implode(',', $fields);
}
public static function bracketed_fields($fields) {
return '('.DB::fields($fields).')';
}
public static function value($str) {
if ($str === null) {
return 'NULL';
} elseif ($str === true) {
return 'true';
} elseif ($str === false) {
return 'false';
} elseif (is_int($str)) {
return $str;
} elseif (is_string($str)) {
return '\''.DB::escape($str).'\'';
} elseif ($str instanceof DBRawString) {
return $str->str;
} else {
return false;
}
}
public static function field_as($field, $name) {
return "{$field} as {$name}";
}
public static function value_as($value, $name) {
return DB::value($value)."as {$name}";
}
public static function if_func($conds, $val1, $val2) {
return 'if('.DB::conds($conds).','.DB::value($val1).','.DB::value($val2).')';
}
public static function setValue($field, $val) {
return $field.' = '.DB::value($val);
}
public static function setValues(array $arr) {
$all = [];
foreach ($arr as $key => $val) {
if (is_int($key)) {
$all[] = $val;
} else {
$all[] = DB::setValue($key, $val);
}
}
return implode(', ', $all);
}
public static function cond($cond) {
if (is_array($cond)) {
if (count($cond) == 3) {
$lhs = $cond[0] instanceof DBRawString ? $cond[0]->str : $cond[0];
$op = $cond[1];
$rhs = DB::value($cond[2]);
return $lhs.' '.$op.' '.$rhs;
} else {
return false;
}
}
return $cond;
}
public static function conds($conds) {
return is_array($conds) ? DB::land($conds) : $conds;
}
public static function land(array $conds) {
if (is_assoc($conds)) {
$new_conds = [];
foreach ($conds as $key => $val) {
if (is_int($key)) {
$new_conds[] = $val;
} else {
if ($val !== null) {
$new_conds[] = [$key, '=', $val];
} else {
$new_conds[] = [$key, 'is', $val];
}
}
}
$conds = $new_conds;
}
return '('.implode(' and ', array_map('DB::cond', $conds)).')';
}
public static function lor(array $conds) {
if (is_assoc($conds)) {
$new_conds = [];
foreach ($conds as $key => $val) {
if (is_int($key)) {
$new_conds[] = $val;
} else {
if ($val !== null) {
$new_conds[] = [$key, '=', $val];
} else {
$new_conds[] = [$key, 'is', $val];
}
}
}
$conds = $new_conds;
}
return '('.implode(' or ', array_map('DB::cond', $conds)).')';
}
public static function tuple(array $vals) {
$str = '(';
$first = true;
foreach ($vals as $val) {
if (!$first) {
$str .= ',';
} else {
$first = false;
}
$str .= DB::value($val);
}
$str .= ')';
return $str;
}
public static function tuples(array $tuples) {
$all = [];
foreach ($tuples as $vals) {
$all[] = DB::tuple($vals);
}
return implode(', ', $all);
}
public static function fetch($res, $opt = DB::ASSOC) {
return $res->fetch_array($opt);
}
public static function bracket($q) {
return '('.DB::query_str($q).')';
}
public static function query_str($q) {
if (is_array($q)) {
$last = '';
$qn = 0;
$use_local_cache = false;
foreach ($q as $val) {
if (is_array($val)) {
if ($last !== 'set' && $last !== "on duplicate key update") {
$val = DB::land($val);
} else {
$val = DB::setValues($val);
}
} elseif ($val instanceof DBUseLocalCache) {
$use_local_cache = true;
$val = $val->str;
}
$last = $val;
if ($val !== '') {
$q[$qn++] = $val;
}
}
array_splice($q, $qn);
$q = implode(' ', $q);
if ($use_local_cache) {
$q = new DBUseLocalCache($q);
}
return $q;
}
return $q;
}
public static function exists($q) {
return 'exists '.DB::bracket($q);
} }
public static function query($q) { public static function query($q) {
global $uojMySQL; return DB::$conn->query(DB::query_str($q));
return mysqli_query($uojMySQL, $q);
} }
public static function update($q) { public static function update($q) {
global $uojMySQL; $ret = DB::$conn->query(DB::query_str($q));
return mysqli_query($uojMySQL, $q); if ($ret === false) {
UOJLog::error(DB::query_str($q));
UOJLog::error('update failed: '.DB::$conn->error);
}
return $ret;
} }
public static function insert($q) { public static function insert($q) {
global $uojMySQL; $ret = DB::$conn->query(DB::query_str($q));
return mysqli_query($uojMySQL, $q); if ($ret === false) {
UOJLog::error(DB::query_str($q));
UOJLog::error('insert failed: '.DB::$conn->error);
}
return $ret;
} }
public static function insert_id() { public static function insert_id() {
global $uojMySQL; return DB::$conn->insert_id;
return mysqli_insert_id($uojMySQL);
} }
public static function delete($q) { public static function delete($q) {
global $uojMySQL; $ret = DB::$conn->query(DB::query_str($q));
return mysqli_query($uojMySQL, $q); if ($ret === false) {
UOJLog::error(DB::query_str($q));
UOJLog::error('delete failed: '.DB::$conn->error);
}
return $ret;
} }
public static function select($q) { public static function select($q) {
global $uojMySQL; $q = DB::query_str($q);
return mysqli_query($uojMySQL, $q); if ($q instanceof DBUseLocalCache) {
$q = $q->str;
$use_local_cache = true;
} else {
$use_local_cache = false;
}
if ($use_local_cache && isset(DB::$cache[$q])) {
$res = DB::$cache[$q];
$res->data_seek(0);
$res->field_seek(0);
return $res;
}
$res = DB::$conn->query($q);
if ($use_local_cache) {
DB::$cache[$q] = $res;
}
if ($res === false) {
UOJLog::error($q);
UOJLog::error(DB::$conn->error);
}
return $res;
} }
public static function selectAll($q, $opt = MYSQLI_ASSOC) { public static function selectAll($q, $opt = DB::ASSOC) {
global $uojMySQL; $qres = DB::select($q);
$res = array(); if ($qres === false) {
$qr = mysqli_query($uojMySQL, $q); return false;
while ($row = mysqli_fetch_array($qr, $opt)) { }
// return $qres->fetch_all($opt); not supported
$res = [];
while ($row = $qres->fetch_array($opt)) {
$res[] = $row; $res[] = $row;
} }
return $res; return $res;
} }
public static function selectFirst($q, $opt = MYSQLI_ASSOC) { public static function selectFirst($q, $opt = DB::ASSOC) {
global $uojMySQL; $res = DB::select($q);
return mysqli_fetch_array(mysqli_query($uojMySQL, $q), $opt); if ($res === false) {
return false;
}
return $res->fetch_array($opt);
} }
public static function selectSingle($q) {
$res = DB::select($q);
if ($res === false) {
return false;
}
$row = $res->fetch_row();
if (!$row) {
return false;
}
return $row[0];
}
/**
* perform SQL query $q in the form of select count(*) from XXX where XXX;
*/
public static function selectCount($q) { public static function selectCount($q) {
global $uojMySQL; $res = DB::select($q);
list($cnt) = mysqli_fetch_array(mysqli_query($uojMySQL, $q), MYSQLI_NUM); if ($res === false) {
return false;
}
list($cnt) = $res->fetch_row();
return $cnt; return $cnt;
} }
/**
* perform SQL query: select exists ($q);
*
* on success, returns 0 or 1
* on failure, returns false
*
* @return int|false
*/
public static function selectExists($q) {
$res = DB::select(["select", DB::exists($q)]);
if ($res === false) {
return false;
}
return (int)($res->fetch_row()[0]);
}
public static function limit() {
$num = func_get_args();
if (count($num) == 1) {
return "limit ".((int)$num[0]);
} elseif (count($num) == 2) {
return "limit ".((int)$num[0]).",".((int)$num[1]);
} else {
return false;
}
}
public static function for_share() {
return "for share";
}
public static function for_update() {
return "for update";
}
public static function startTransaction() {
return DB::$conn->begin_transaction();
}
public static function rollback() {
return DB::$conn->rollback();
}
public static function commit() {
return DB::$conn->commit();
}
public static function transaction($func) {
if (DB::$in_transaction) {
$ret = $func();
} else {
DB::$in_transaction = true;
DB::startTransaction();
$ret = $func();
DB::commit();
DB::$in_transaction = false;
}
return $ret;
}
public static function lock($tables, $func) {
$q = [];
foreach ($tables as $table => $type) {
if ($type != DB::WLOCK && $type != DB::RLOCK) {
UOJLog::error('Unknown type: '.$type);
return false;
}
$q[] = $table.' '.$type;
}
$q = 'lock tables '.implode(',', $q);
DB::query($q);
$ret = $func();
DB::query("unlock tables");
return $ret;
}
public static function checkTableExists($name) { public static function checkTableExists($name) {
global $uojMySQL; return DB::select(["select 1 from", DB::table($name)]) !== false;
return DB::query("select 1 from $name") !== false;
} }
public static function num_rows() { public static function num_rows($res) {
global $uojMySQL; return $res->num_rows;
return mysqli_num_rows($uojMySQL);
} }
public static function affected_rows() { public static function affected_rows() {
global $uojMySQL; return DB::$conn->affected_rows;
return mysqli_affected_rows($uojMySQL);
} }
} }

View File

@ -0,0 +1,9 @@
<?php
class DBRawString {
public $str = '';
function __construct($str) {
$this->str = $str;
}
}

View File

@ -0,0 +1,9 @@
<?php
class DBUseLocalCache {
public $str;
function __construct($str) {
$this->str = $str;
}
}

64
web/app/models/FS.php Normal file
View File

@ -0,0 +1,64 @@
<?php
class FS {
public static function scandir(string $directory, $cfg = []) {
$cfg += [
'exclude_dots' => true
];
$entries = scandir($directory);
if ($cfg['exclude_dots']) {
$entries = array_filter($entries, fn($name) => $name !== '.' && $name !== '..');
}
return $entries;
}
public static function scandir_r(string $directory, $cfg = []) {
foreach (FS::scandir($directory, $cfg) as $name) {
$cur = "{$directory}/{$name}";
if (is_dir($cur)) {
foreach (FS::scandir_r($cur, $cfg) as $sub) {
yield "{$name}/{$sub}";
}
} else {
yield $name;
}
}
}
/**
* @param int $type lock type. can be either LOCK_SH or LOCK_EX
*/
public static function lock_file(string $path, int $type, callable $func) {
$lock_fp = fopen($path, 'c');
if (!flock($lock_fp, $type | LOCK_NB)) {
UOJLog::error("lock failed: {$path}");
return false;
}
$ret = $func();
flock($lock_fp, LOCK_UN | LOCK_NB);
return $ret;
}
public static function randomAvailableFileName($dir, $suffix = '') {
do {
$name = $dir . uojRandString(20) . $suffix;
} while (file_exists(UOJContext::storagePath().$name));
return $name;
}
public static function randomAvailableTmpFileName() {
return static::randomAvailableFileName('/tmp/');
}
public static function randomAvailableSubmissionFileName() {
$num = uojRand(1, 10000);
if (!file_exists(UOJContext::storagePath()."/submission/$num")) {
system("mkdir ".UOJContext::storagePath()."/submission/$num");
}
return static::randomAvailableFileName("/submission/$num/");
}
}

View File

@ -1,14 +1,74 @@
<?php <?php
class HTML { class HTML {
public static function escape($str) { public static function escape(?string $str, $cfg = []) {
return htmlspecialchars($str); if ($str === null) {
return '';
} else {
if (!empty($cfg['single_line'])) {
$str = str_replace(["\n", "\r"], '', $str);
}
return htmlspecialchars($str);
}
} }
public static function stripTags($str) { public static function stripTags($str) {
return strip_tags($str); return strip_tags($str);
} }
public static function protocol(string $loc = 'main') {
if (UOJConfig::$data['web'][$loc]['protocol'] === 'http/https') {
if (UOJContext::isUsingHttps()) {
return 'https';
} else {
return 'http';
}
} else {
return UOJConfig::$data['web'][$loc]['protocol'];
}
}
public static function port(string $loc = 'main') {
if (UOJConfig::$data['web'][$loc]['port'] === '80/443') {
return HTML::standard_port(HTML::protocol($loc));
} else {
return UOJConfig::$data['web'][$loc]['port'];
}
}
public static function standard_port(string $protocol) {
if ($protocol === 'http') {
return 80;
} elseif ($protocol === 'https') {
return 443;
} else {
return null;
}
}
public static function attr($attr) {
$html = '';
foreach ($attr as $key => $val) {
$html .= ' ' . $key . '="';
$html .= HTML::escape(is_array($val) ? implode(' ', $val) : $val);
$html .= '"';
}
return $html;
}
public static function tag_begin(string $name, array $attr = []) {
return '<' . $name . HTML::attr($attr) . '>';
}
public static function tag_end(string $name) {
return '</' . $name . '>';
}
public static function tag(string $name, array $attr, $content) {
return HTML::tag_begin($name, $attr) . $content . HTML::tag_end($name);
}
public static function empty_tag(string $name, array $attr) {
return '<' . $name . HTML::attr($attr) . ' />';
}
public static function avatar_addr($user, $size) { public static function avatar_addr($user, $size) {
if ($user['avatar_source'] == 'qq' && $user['qq']) { $extra = UOJUser::getExtra($user);
if ($extra['avatar_source'] == 'qq' && $user['qq']) {
$s = '5'; $s = '5';
if ($size <= 40) { if ($size <= 40) {
@ -18,7 +78,7 @@ class HTML {
} elseif ($size <= 140) { } elseif ($size <= 140) {
$s = '4'; $s = '4';
} }
return "https://q1.qlogo.cn/g?b=qq&nk={$user['qq']}&s=$s"; return "https://q1.qlogo.cn/g?b=qq&nk={$user['qq']}&s=$s";
} }
@ -26,9 +86,9 @@ class HTML {
} }
public static function tablist($tabs_info, $cur, $type = 'nav-tabs') { public static function tablist($tabs_info, $cur, $type = 'nav-tabs') {
$html = '<ul class="nav '.$type.'" role="tablist">'; $html = '<ul class="nav ' . $type . '" role="tablist">';
foreach ($tabs_info as $id => $tab) { foreach ($tabs_info as $id => $tab) {
$html .= '<li class="nav-item"><a class="nav-link'.($cur == $id ? ' active' : '').'" href="'.$tab['url'].'" role="tab">'.$tab['name'].'</a></li>'; $html .= '<li class="nav-item"><a class="nav-link' . ($cur == $id ? ' active' : '') . '" href="' . $tab['url'] . '" role="tab">' . $tab['name'] . '</a></li>';
} }
$html .= '</ul>'; $html .= '</ul>';
return $html; return $html;
@ -37,58 +97,76 @@ class HTML {
public static function navListGroup($tabs_info, $cur) { public static function navListGroup($tabs_info, $cur) {
$html = '<div class="list-group">'; $html = '<div class="list-group">';
foreach ($tabs_info as $id => $tab) { foreach ($tabs_info as $id => $tab) {
$html .= '<a role="button" class="list-group-item list-group-item-action'.($cur == $id ? ' active' : '').'" href="'.$tab['url'].'">'.$tab['name'].'</a>'; $html .= '<a role="button" class="list-group-item list-group-item-action' . ($cur == $id ? ' active' : '') . '" href="' . $tab['url'] . '">' . $tab['name'] . '</a>';
} }
$html .= '</div>'; $html .= '</div>';
return $html; return $html;
} }
public static function hiddenToken() { public static function hiddenToken() {
return '<input type="hidden" name="_token" value="'.crsf_token().'" />'; return '<input type="hidden" name="_token" value="' . crsf_token() . '" />';
} }
public static function div_vinput($name, $type, $label_text, $default_value) { public static function div_vinput($name, $type, $label_text, $default_value) {
return '<div id="'."div-$name".'" class="mb-3">' return '<div id="' . "div-$name" . '" class="mb-3">'
. '<label for="'."input-$name".'" class="control-label form-label">'.$label_text.'</label>' . '<label for="' . "input-$name" . '" class="control-label form-label">' . $label_text . '</label>'
. '<input type="'.$type.'" class="form-control" name="'.$name.'" id="'."input-$name".'" value="'.HTML::escape($default_value).'" />' . '<input type="' . $type . '" class="form-control" name="' . $name . '" id="' . "input-$name" . '" value="' . HTML::escape($default_value) . '" />'
. '<span class="help-block invalid-feedback" id="'."help-$name".'"></span>' . '<span class="help-block invalid-feedback" id="' . "help-$name" . '"></span>'
. '</div>'; . '</div>';
} }
public static function div_vtextarea($name, $label_text, $default_value) { public static function div_vtextarea($name, $label_text, $default_value) {
return '<div id="'."div-$name".'" class="mb-3">' return '<div id="' . "div-$name" . '" class="mb-3">'
. '<label for="'."input-$name".'" class="control-label">'.$label_text.'</label>' . '<label for="' . "input-$name" . '" class="control-label">' . $label_text . '</label>'
. '<textarea class="form-control" name="'.$name.'" id="'."input-$name".'">'.HTML::escape($default_value).'</textarea>' . '<textarea class="form-control" name="' . $name . '" id="' . "input-$name" . '">' . HTML::escape($default_value) . '</textarea>'
. '<span class="help-block" id="'."help-$name".'"></span>' . '<span class="help-block" id="' . "help-$name" . '"></span>'
. '</div>'; . '</div>';
} }
public static function checkbox($name, $default_value) { public static function checkbox($name, $default_value) {
$status = $default_value ? 'checked="checked" ' : ''; $status = $default_value ? 'checked="checked" ' : '';
return '<input type="checkbox" id="'."input-$name".'" name="'.$name.'" '.$status.'/>'; return '<input class="form-check-input" type="checkbox" id="' . "input-$name" . '" name="' . $name . '" ' . $status . '/>';
} }
public static function option($value, $text, $selected) {
public static function blog_url($username, $uri) { return '<option value="' . HTML::escape($value) . '"'
. ($selected ? ' selected="selected"' : '') . '>'
. HTML::escape($text)
. '</option>';
}
public static function tr_none() {
return '<tr class="text-center"><td colspan="233">' . UOJLocale::get('none') . '</td></tr>';
}
public static function blog_url($username, $uri, array $cfg = []) {
$cfg += [
'escape' => true
];
switch (UOJConfig::$data['switch']['blog-domain-mode']) { switch (UOJConfig::$data['switch']['blog-domain-mode']) {
case 1: case 1:
$port = ((UOJConfig::$data['web']['blog']['protocol'] === "http" && UOJConfig::$data['web']['blog']['port'] == 80) || (UOJConfig::$data['web']['blog']['protocol'] === "https" && UOJConfig::$data['web']['blog']['port'] == 443)) ? '' : (':'.UOJConfig::$data['web']['blog']['port']); $port = ((UOJConfig::$data['web']['blog']['protocol'] === "http" && UOJConfig::$data['web']['blog']['port'] == 80) || (UOJConfig::$data['web']['blog']['protocol'] === "https" && UOJConfig::$data['web']['blog']['port'] == 443)) ? '' : (':' . UOJConfig::$data['web']['blog']['port']);
$url = UOJConfig::$data['web']['blog']['protocol'].'://'.blog_name_encode($username).'.'.UOJConfig::$data['web']['blog']['host'].$port; $url = UOJConfig::$data['web']['blog']['protocol'] . '://' . blog_name_encode($username) . '.' . UOJConfig::$data['web']['blog']['host'] . $port;
break; break;
case 2: case 2:
$port = ((UOJConfig::$data['web']['blog']['protocol'] === "http" && UOJConfig::$data['web']['blog']['port'] == 80) || (UOJConfig::$data['web']['blog']['protocol'] === "https" && UOJConfig::$data['web']['blog']['port'] == 443)) ? '' : (':'.UOJConfig::$data['web']['blog']['port']); $port = ((UOJConfig::$data['web']['blog']['protocol'] === "http" && UOJConfig::$data['web']['blog']['port'] == 80) || (UOJConfig::$data['web']['blog']['protocol'] === "https" && UOJConfig::$data['web']['blog']['port'] == 443)) ? '' : (':' . UOJConfig::$data['web']['blog']['port']);
$url = UOJConfig::$data['web']['blog']['protocol'].'://'.UOJConfig::$data['web']['blog']['host'].$port.'/'.blog_name_encode($username); $url = UOJConfig::$data['web']['blog']['protocol'] . '://' . UOJConfig::$data['web']['blog']['host'] . $port . '/' . blog_name_encode($username);
break; break;
case 3: case 3:
$url = HTML::url('/blog/'.blog_name_encode($username)); $url = HTML::url('/blog/' . blog_name_encode($username));
break; break;
} }
$url .= $uri; $url .= $uri;
$url = rtrim($url, '/'); $url = rtrim($url, '/');
return HTML::escape($url);
if ($cfg['escape']) {
$url = HTML::escape($url);
}
return $url;
} }
public static function blog_list_url() { public static function blog_list_url() {
switch (UOJConfig::$data['switch']['blog-domain-mode']) { switch (UOJConfig::$data['switch']['blog-domain-mode']) {
case 1: case 1:
case 2: case 2:
$port = ((UOJConfig::$data['web']['blog']['protocol'] === "http" && UOJConfig::$data['web']['blog']['port'] == 80) || (UOJConfig::$data['web']['blog']['protocol'] === "https" && UOJConfig::$data['web']['blog']['port'] == 443)) ? '' : (':'.UOJConfig::$data['web']['blog']['port']); $port = ((UOJConfig::$data['web']['blog']['protocol'] === "http" && UOJConfig::$data['web']['blog']['port'] == 80) || (UOJConfig::$data['web']['blog']['protocol'] === "https" && UOJConfig::$data['web']['blog']['port'] == 443)) ? '' : (':' . UOJConfig::$data['web']['blog']['port']);
$url = UOJConfig::$data['web']['blog']['protocol'].'://'.UOJConfig::$data['web']['blog']['host'].$port; $url = UOJConfig::$data['web']['blog']['protocol'] . '://' . UOJConfig::$data['web']['blog']['host'] . $port;
break; break;
case 3: case 3:
$url = HTML::url('/blogs'); $url = HTML::url('/blogs');
@ -97,52 +175,216 @@ class HTML {
return HTML::escape(rtrim($url, '/')); return HTML::escape(rtrim($url, '/'));
} }
public static function url($uri, $config = array()) { public static function url($uri, array $cfg = []) {
$config = array_merge(array( $cfg += [
'location' => 'main', 'location' => 'main',
'params' => null 'params' => null,
), $config); 'remove_all_params' => false,
'with_token' => false,
'escape' => true
];
$path = strtok($uri, '?'); if ($cfg['location'] == 'cdn' && !UOJContext::hasCDN()) {
$qs = strtok('?'); $cfg['location'] = 'main';
parse_str($qs, $param);
if ($config['params'] != null) {
$param = array_merge($param, $config['params']);
} }
$url = ''; // '//'.UOJConfig::$data['web'][$config['location']]['host']; if (strStartWith($uri, '?')) {
$path = strtok(UOJContext::requestURI(), '?');
$qs = strtok($uri, '?');
} else {
$path = strtok($uri, '?');
$qs = strtok('?');
}
parse_str($qs, $param);
if ($cfg['remove_all_params']) {
$param = [];
} elseif ($cfg['params'] != null) {
$param = array_merge($param, $cfg['params']);
}
if ($cfg['with_token']) {
$param['_token'] = crsf_token();
}
// $url = '//'.UOJConfig::$data['web'][$config['location']]['host'];
// if (HTML::port($cfg['location']) != HTML::standard_port($protocol)) {
// $url .= ':'.HTML::port($cfg['location']);
// }
$url = '';
if ($param) { if ($param) {
$url .= $path.'?'.HTML::query_string_encode($param); $url .= $path . '?' . HTML::query_string_encode($param);
} elseif ($path != '/') { } elseif ($path != '/') {
$url .= rtrim($path, '/'); $url .= rtrim($path, '/');
} else { } else {
$url .= $path; $url .= $path;
} }
return HTML::escape($url);
} if ($cfg['escape']) {
public static function timeanddate_url($time, $config = array()) { $url = HTML::escape($url);
$url = UOJConfig::$data['web']['blog']['protocol'].'://';
$url .= 'www.timeanddate.com/worldclock/fixedtime.html';
$url .= '?'.'iso='.$time->format('Ymd\THi');
$url .= '&'.'p1=33';
if (isset($config['duration']) && $config['duration'] < 3600) {
$url .= '&'.'ah='.floor($config['duration'] / 60);
if ($config['duration'] % 60 != 0) {
$url .= '&'.'am='.($config['duration'] % 60);
}
} }
$url = HTML::escape($url);
return $url; return $url;
} }
public static function timeanddate_url(DateTime $time, array $cfg = []) {
public static function js_src($uri, $config = array('location' => 'main')) { $url = HTML::protocol() . '://';
return '<script src="'.HTML::url($uri, $config).'"></script>'; $url .= 'www.timeanddate.com/worldclock/fixedtime.html';
$url .= '?' . 'iso=' . $time->format('Ymd\THi');
$url .= '&' . 'p1=33';
if (isset($cfg['duration']) && $cfg['duration'] < 3600) {
$url .= '&' . 'ah=' . floor($cfg['duration'] / 60);
if ($cfg['duration'] % 60 != 0) {
$url .= '&' . 'am=' . ($cfg['duration'] % 60);
}
}
return HTML::escape($url);
} }
public static function css_link($uri, $config = array('location' => 'main')) {
return '<link type="text/css" rel="stylesheet" href="'.HTML::url($uri, $config).'" />'; public static function relative_time_str($time, $gran = -1) {
$d = [
[1, 'seconds'],
[60, 'minutes'],
[3600, 'hours'],
[86400, 'days'],
[604800, 'weeks'],
[2592000, 'months'],
[31536000, 'years'],
];
$w = [];
$res = "";
$diff = time() - $time;
$secondsLeft = $diff;
$stopat = 0;
for ($i = 6; $i > $gran; $i--) {
$w[$i] = intval($secondsLeft / $d[$i][0]);
$secondsLeft -= ($w[$i] * $d[$i][0]);
if ($w[$i] != 0) {
$res .= UOJLocale::get('time::x ' . $d[$i][1], abs($w[$i])) . " ";
switch ($i) {
case 6: // shows years and months
if ($stopat == 0) {
$stopat = 5;
}
break;
case 5: // shows months and weeks
if ($stopat == 0) {
$stopat = 4;
}
break;
case 4: // shows weeks and days
if ($stopat == 0) {
$stopat = 3;
}
break;
case 3: // shows days and hours
if ($stopat == 0) {
$stopat = 2;
}
break;
case 2: // shows hours and minutes
if ($stopat == 0) {
$stopat = 1;
}
break;
case 1: // shows minutes and seconds if granularity is not set higher
break;
}
if ($i === $stopat) {
break;
}
}
}
$res .= ($diff > 0) ? UOJLocale::get('time::ago') : UOJLocale::get('time::left');
return $res;
} }
public static function link(?string $uri, $text, $cfg = ['location' => 'main']) {
if ($uri === null) {
return '<a>' . HTML::escape($text) . '</a>';
}
return '<a class="text-decoration-none" href="' . HTML::url($uri, $cfg) . '">' . HTML::escape($text) . '</a>';
}
public static function autolink(string $url, array $attr = []) {
return '<a class="text-decoration-none" href="' . $url . '"' . HTML::attr($attr) . '>' . $url . '</a>';
}
public static function js_src(string $uri, array $cfg = []) {
$cfg += [
'location' => 'cdn',
'async' => false
];
$async = empty($cfg['async']) ? '' : 'async ';
return '<script ' . $async . 'src="' . HTML::url($uri, $cfg) . '"></script>';
}
public static function css_link(string $uri, $cfg = []) {
$cfg += ['location' => 'cdn'];
return '<link rel="stylesheet" href="' . HTML::url($uri, $cfg) . '" />';
}
public static function table($header, iterable $data, $cfg = []) {
mergeConfig($cfg, [
'th' => function ($c) {
return "<th>{$c}</th>";
},
'td' => function ($d) {
return "<td>{$d}</td>";
},
'tr' => false, // if tr is a function, then td and tr_attr is disabled
'empty' => 'HTML::tr_none',
'table_attr' => [
'class' => ['table'],
],
'thead_attr' => [],
'tbody_attr' => [],
'tr_attr' => function ($row, $idx) {
return [];
}
]);
$html = HTML::tag_begin('table', $cfg['table_attr']);
$html .= HTML::tag_begin('thead', $cfg['thead_attr']);
if (is_array($header)) {
$html .= '<tr>' . implode(' ', array_map($cfg['th'], array_values($header), array_keys($header))) . '</tr>';
} else {
$html .= $header;
}
$html .= HTML::tag_end('thead');
$html .= HTML::tag_begin('tbody', $cfg['tbody_attr']);
if (is_iterable($data)) {
$data_html = [];
if (is_callable($cfg['tr'])) {
foreach ($data as $idx => $row) {
$data_html[] = $cfg['tr']($row, $idx);
}
} else {
foreach ($data as $idx => $row) {
$data_html[] = HTML::tag_begin('tr', $cfg['tr_attr']($row, $idx));
if (is_array($row)) {
foreach ($row as $cidx => $c) {
$data_html[] = $cfg['td']($c, $cidx);
}
} else {
$data_html[] = $row;
}
$data_html[] = HTML::tag_end('tr');
}
}
$data_html = implode($data_html);
} else {
$data_html = $data;
}
$html .= $data_html !== '' ? $data_html : $cfg['empty']();
$html .= HTML::tag_end('tbody');
$html .= HTML::tag_end('table');
return $html;
}
public static function responsive_table($header, $data, $cfg = []) {
return HTML::tag_begin('div', ['class' => 'table-responsive']) . HTML::table($header, $data, $cfg) . HTML::tag_end('div');
}
public static function query_string_encode($q, $array_name = null) { public static function query_string_encode($q, $array_name = null) {
if (!is_array($q)) { if (!is_array($q)) {
return false; return false;
@ -151,32 +393,32 @@ class HTML {
foreach ((array)$q as $k => $v) { foreach ((array)$q as $k => $v) {
if ($array_name !== null) { if ($array_name !== null) {
if (is_numeric($k)) { if (is_numeric($k)) {
$k = $array_name."[]"; $k = $array_name . "[]";
} else { } else {
$k = $array_name."[$k]"; $k = $array_name . "[$k]";
} }
} }
if (is_array($v) || is_object($v)) { if (is_array($v) || is_object($v)) {
$r[] = self::query_string_encode($v, $k); $r[] = self::query_string_encode($v, $k);
} else { } else {
$r[] = urlencode($k)."=".urlencode($v); $r[] = urlencode($k) . "=" . urlencode($v);
} }
} }
return implode("&", $r); return implode("&", $r);
} }
public static function purifier() { public static function purifier() {
$config = HTMLPurifier_Config::createDefault(); $config = HTMLPurifier_Config::createDefault();
$config->set('Output.Newline', true); $config->set('Output.Newline', true);
$def = $config->getHTMLDefinition(true); $def = $config->getHTMLDefinition(true);
$def->addElement('section', 'Block', 'Flow', 'Common'); $def->addElement('section', 'Block', 'Flow', 'Common');
$def->addElement('nav', 'Block', 'Flow', 'Common'); $def->addElement('nav', 'Block', 'Flow', 'Common');
$def->addElement('article', 'Block', 'Flow', 'Common'); $def->addElement('article', 'Block', 'Flow', 'Common');
$def->addElement('aside', 'Block', 'Flow', 'Common'); $def->addElement('aside', 'Block', 'Flow', 'Common');
$def->addElement('header', 'Block', 'Flow', 'Common'); $def->addElement('header', 'Block', 'Flow', 'Common');
$def->addElement('footer', 'Block', 'Flow', 'Common'); $def->addElement('footer', 'Block', 'Flow', 'Common');
$extra_allowed_html = [ $extra_allowed_html = [
'span' => ['data-realname' => 'Text', 'data-uoj-username' => 'Number'], 'span' => ['data-realname' => 'Text', 'data-uoj-username' => 'Number'],
]; ];
@ -186,7 +428,7 @@ class HTML {
$def->addAttribute($element, $attribute, $type); $def->addAttribute($element, $attribute, $type);
} }
} }
return new HTMLPurifier($config); return new HTMLPurifier($config);
} }
@ -206,10 +448,10 @@ class HTML {
'br' => [], 'br' => [],
'span' => ['data-realname' => 'Text', 'data-uoj-username' => 'Number'], 'span' => ['data-realname' => 'Text', 'data-uoj-username' => 'Number'],
]; ];
$allowed_elements = []; $allowed_elements = [];
$allowed_attributes = []; $allowed_attributes = [];
foreach ($allowed_html as $element => $attributes) { foreach ($allowed_html as $element => $attributes) {
$allowed_elements[$element] = true; $allowed_elements[$element] = true;
foreach ($attributes as $attribute => $type) { foreach ($attributes as $attribute => $type) {
@ -239,4 +481,35 @@ class HTML {
] ]
]); ]);
} }
public static function echoPanel($cls = [], $title, $body, $other = null) {
if (is_string($cls)) {
$cls = ['card' => $cls];
}
$cls += [
'card' => '',
'header' => '',
'body' => '',
];
echo '<div class="card ', $cls['card'], '">';
echo '<div class="card-header fw-bold ', $cls['header'], '">', $title, '</div>';
if ($body !== null) {
echo '<div class="card-body ', $cls['body'], '">';
if (is_string($body)) {
echo $body;
} else {
$body();
}
echo '</div>';
}
if ($other !== null) {
if (is_string($other)) {
echo $other;
} else {
$other();
}
}
echo '</div>';
}
} }

View File

@ -7,56 +7,125 @@ class Paginator {
public $cur_page; public $cur_page;
public $cur_start; public $cur_start;
public $max_extend; public $max_extend;
public $table_name;
public $cond;
public $col_names;
public $tail;
public $post_filter;
public $table; public $table;
public function getPage($page) {
$cur_start = ($page - 1) * $this->page_len;
$table = DB::selectAll([
"select", DB::fields($this->col_names), "from", DB::query_str($this->table_name),
"where", $this->cond,
$this->tail, DB::limit($cur_start, $this->page_len)
]);
if ($this->post_filter === null) {
return $table;
} else {
return array_filter($table, $this->post_filter);
}
}
public function checkPageExists($page) {
if ($page < 1 || $page > $this->n_pages) {
return false;
}
if ($this->post_filter === null) {
return true;
}
$post_filter = $this->post_filter;
$cur_start = ($page - 1) * $this->page_len;
for ($bg = 0; $bg < $this->page_len; $bg += 10) {
$table = DB::selectAll([
"select", DB::fields($this->col_names), "from", DB::query_str($this->table_name),
"where", $this->cond,
$this->tail, DB::limit($cur_start + $bg, min(10, $this->page_len - $bg))
]);
foreach ($table as $entry) {
if ($post_filter($entry)) {
return true;
}
}
}
return false;
}
public function __construct($config) { public function __construct($config) {
$this->max_extend = isset($config['max_extend']) ? (int)$config['max_extend'] : 5;
$this->post_filter = isset($config['post_filter']) ? $config['post_filter'] : null;
if (isset($config['data'])) { if (isset($config['data'])) {
$this->n_pages = 1; $this->n_pages = 1;
$this->cur_page = 1; $this->cur_page = 1;
$this->cur_start = 0; $this->cur_start = 0;
$this->table = $config['data']; $this->table = $config['data'];
} elseif (!isset($config['echo_full'])) { if ($this->post_filter !== null) {
$table = $config['table_name']; $this->table = array_filter($this->table, $this->post_filter);
$cond = isset($config['cond']) ? $config['cond'] : '1';
if (isset($config['pagination_table'])) {
$table = $config['pagination_table'];
$cond = isset($config['pagination_cond']) ? $config['pagination_cond'] : $cond;
} }
$this->n_rows = DB::selectCount("select count(*) from {$table} where {$cond}"); } elseif (!isset($config['echo_full'])) {
$this->cur_page = UOJRequest::get('page', 'validateUInt', 1);
$this->page_len = isset($config['page_len']) ? $config['page_len'] : 10;
$this->n_pages = max((int)ceil($this->n_rows / $this->page_len), 1);
$this->cur_page = validateUInt($_GET['page']) ? (int)$_GET['page'] : 1;
if ($this->cur_page < 1) { if ($this->cur_page < 1) {
$this->cur_page = 1; $this->cur_page = 1;
} elseif ($this->cur_page > $this->n_pages) { }
$this->page_len = isset($config['page_len']) ? $config['page_len'] : 10;
$this->table_name = $config['table_name'];
$this->cond = $config['cond'];
$this->col_names = $config['col_names'];
$this->tail = $config['tail'];
$this->n_rows = DB::selectCount([
"select", "count(*)",
"from", DB::query_str($this->table_name),
"where", $this->cond,
$this->tail,
]);
$this->n_pages = max((int)ceil($this->n_rows / $this->page_len), 1);
while ($this->n_pages > 1 && !$this->checkPageExists($this->n_pages)) {
$this->n_pages--;
}
if ($this->cur_page > $this->n_pages) {
$this->cur_page = $this->n_pages; $this->cur_page = $this->n_pages;
} }
while (true) {
$this->table = $this->getPage($this->cur_page);
if ($this->table || $this->cur_page == 1) {
break;
}
$this->cur_page--;
}
$this->cur_start = ($this->cur_page - 1) * $this->page_len; $this->cur_start = ($this->cur_page - 1) * $this->page_len;
$this->table = DB::selectAll("select ".join($config['col_names'], ',')." from {$config['table_name']} where {$config['cond']} {$config['tail']} limit {$this->cur_start}, {$this->page_len}");
} else { } else {
$this->n_pages = 1; $this->n_pages = 1;
$this->cur_page = 1; $this->cur_page = 1;
$this->cur_start = ($this->cur_page - 1) * $this->page_len; $this->cur_start = 0;
$this->table = DB::selectAll("select ".join($config['col_names'], ',')." from {$config['table_name']} where {$config['cond']} {$config['tail']}"); $this->table = DB::selectAll([
"select", DB::fields($config['col_names']), "from", DB::query_str($config['table_name']),
"where", $config['cond'],
$config['tail']
]);
if ($this->post_filter !== null) {
$this->table = array_filter($this->table, $this->post_filter);
}
} }
$this->max_extend = isset($config['max_extend']) ? (int)$config['max_extend'] : 5;
} }
public function getPageRawUri($page) { public function getPageRawUri($page) {
$path = strtok($_SERVER["REQUEST_URI"], '?'); $path = strtok($_SERVER["REQUEST_URI"], '?');
$query_string = strtok('?'); $query_string = strtok('?');
parse_str($query_string, $param); parse_str($query_string, $param);
$param['page'] = $page; $param['page'] = $page;
if ($page == 1) { if ($page == 1) {
unset($param['page']); unset($param['page']);
} }
if ($param) { if ($param) {
return $path . '?' . http_build_query($param); return $path . '?' . http_build_query($param);
} else { } else {
@ -66,102 +135,74 @@ class Paginator {
public function getPageUri($page) { public function getPageUri($page) {
return HTML::escape($this->getPageRawUri($page)); return HTML::escape($this->getPageRawUri($page));
} }
public function get() { public function get($limit = -1) {
$cur_idx = $this->cur_start + 1; $cur_idx = $this->cur_start + 1;
foreach ($this->table as $idx => $row) { foreach ($this->table as $row) {
if ($limit != -1 && $cur_idx - $this->cur_start > $limit) {
break;
}
yield $cur_idx++ => $row; yield $cur_idx++ => $row;
} }
} }
public function isEmpty() { public function isEmpty() {
return empty($this->table); return empty($this->table);
} }
public function countInCurPage() {
public function pagination() { return count($this->table);
global $REQUIRE_LIB; }
public function pagination() {
if ($this->n_pages == 1) { if ($this->n_pages == 1) {
return ''; return '';
} }
if (isset($REQUIRE_LIB['bootstrap5'])) { $prev_page = false;
$html = '<ul class="pagination my-0 justify-content-center">'; $next_page = false;
} else { $main_lis = '';
$html = '<ul class="pagination top-buffer-no bot-buffer-sm justify-content-center">';
}
if ($this->cur_page > 1) {
$html .= '<li class="page-item">'
. '<a class="page-link" href="'.$this->getPageUri(1).'">';
if (isset($REQUIRE_LIB['bootstrap5'])) {
$html .= '<i class="bi bi-chevron-double-left"></i>';
} else {
$html .= '<span class="glyphicon glyphicon glyphicon-fast-backward"></span>';
}
$html .= '</a></li>';
$html .= '<li class="page-item">'
. '<a class="page-link" href="'.$this->getPageUri($this->cur_page - 1).'">';
if (isset($REQUIRE_LIB['bootstrap5'])) {
$html .= '<i class="bi bi-chevron-left"></i>';
} else {
$html .= '<span class="glyphicon glyphicon glyphicon-backward"></span>';
}
$html .= '</a></li>';
} else {
$html .= '<li class="page-item disabled"><a class="page-link">';
if (isset($REQUIRE_LIB['bootstrap5'])) {
$html .= '<i class="bi bi-chevron-double-left"></i>';
} else {
$html .= '<span class="glyphicon glyphicon glyphicon-fast-backward"></span>';
}
$html .= '</a></li>';
$html .= '<li class="page-item disabled"><a class="page-link">';
if (isset($REQUIRE_LIB['bootstrap5'])) {
$html .= '<i class="bi bi-chevron-left"></i>';
} else {
$html .= '<span class="glyphicon glyphicon glyphicon-backward"></span>';
}
$html .= '</a></li>';
}
for ($i = max($this->cur_page - $this->max_extend, 1); $i <= min($this->cur_page + $this->max_extend, $this->n_pages); $i++) {
if ($i == $this->cur_page) {
$html .= '<li class="page-item active"><a class="page-link" href="'.$this->getPageUri($i).'">'.$i.'</a></li>';
} else {
$html .= '<li class="page-item"><a class="page-link" href="'.$this->getPageUri($i).'">'.$i.'</a></li>';
}
}
if ($this->cur_page < $this->n_pages) {
$html .= '<li class="page-item">'
. '<a class="page-link" href="'.$this->getPageUri($this->cur_page + 1).'">';
if (isset($REQUIRE_LIB['bootstrap5'])) {
$html .= '<i class="bi bi-chevron-right"></i>';
} else {
$html .= '<span class="glyphicon glyphicon glyphicon-forward"></span>';
}
$html .= '</a></li>';
$html .= '<li class="page-item">' $page_st = $this->cur_page - $this->max_extend;
. '<a class="page-link" href="'.$this->getPageUri($this->n_pages).'">'; $page_ed = $this->cur_page + $this->max_extend;
if (isset($REQUIRE_LIB['bootstrap5'])) { if ($this->n_pages <= $this->max_extend * 2 + 1) {
$html .= '<i class="bi bi-chevron-double-right"></i>'; $page_st = 1;
$page_ed = $this->n_pages;
} elseif ($page_st < 1) {
$page_st = 1;
$page_ed = $this->max_extend * 2 + 1;
} elseif ($page_ed > $this->n_pages) {
$page_st = $this->n_pages - $this->max_extend * 2;
$page_ed = $this->n_pages;
}
for ($i = $page_st; $i <= $page_ed; $i++) {
if ($i == $this->cur_page) {
$main_lis .= '<li class="page-item active"><a class="page-link" href="' . $this->getPageUri($i) . '">' . $i . '</a></li>';
} else { } else {
$html .= '<span class="glyphicon glyphicon glyphicon-fast-forward"></span>'; if ($this->checkPageExists($i)) {
$main_lis .= '<li class="page-item"><a class="page-link" href="' . $this->getPageUri($i) . '">' . $i . '</a></li>';
if ($i < $this->cur_page) {
$prev_page = $i;
} elseif ($next_page === false) {
$next_page = $i;
}
}
} }
$html .= '</a></li>'; }
$html = '';
$html .= '<ul class="pagination my-0 justify-content-center">';
if ($prev_page !== false) {
$html .= '<li class="page-item"><a class="page-link" href="' . $this->getPageUri(1) . '""><i class="bi bi-chevron-double-left"></i></a></li>';
$html .= '<li class="page-item"><a class="page-link" href="' . $this->getPageUri($prev_page) . '"><i class="bi bi-chevron-left"></i></a></li>';
} else { } else {
$html .= '<li class="page-item disabled"><a class="page-link">'; $html .= '<li class="page-item disabled"><a class="page-link"><i class="bi bi-chevron-double-left"></i></a></li>';
if (isset($REQUIRE_LIB['bootstrap5'])) { $html .= '<li class="page-item disabled"><a class="page-link"><i class="bi bi-chevron-left"></i></a></li>';
$html .= '<i class="bi bi-chevron-right"></i>'; }
} else { $html .= $main_lis;
$html .= '<span class="glyphicon glyphicon glyphicon-forward"></span>'; if ($next_page !== false) {
} $html .= '<li class="page-item"><a class="page-link" href="' . $this->getPageUri($next_page) . '"><i class="bi bi-chevron-right"></i></a></li>';
$html .= '</a></li>'; $html .= '<li class="page-item"><a class="page-link" href="' . $this->getPageUri($this->n_pages) . '"><i class="bi bi-chevron-double-right"></i></a></li>';
$html .= '<li class="page-item disabled"><a class="page-link">'; } else {
if (isset($REQUIRE_LIB['bootstrap5'])) { $html .= '<li class="page-item disabled"><a class="page-link"><i class="bi bi-chevron-right"></i></a></li>';
$html .= '<i class="bi bi-chevron-double-right"></i>'; $html .= '<li class="page-item disabled"><a class="page-link"><i class="bi bi-chevron-double-right"></i></a></li>';
} else {
$html .= '<span class="glyphicon glyphicon glyphicon-fast-forward"></span>';
}
$html .= '</a></li>';
} }
$html .= '</ul>'; $html .= '</ul>';
return $html; return $html;

View File

@ -1,15 +1,15 @@
<?php <?php
class Route { class Route {
protected static $routes = array(); protected static $routes = [];
protected static $patterns = array(); protected static $patterns = [];
protected static $groupStack = array(array()); protected static $groupStack = [[]];
public static function match($methods, $uri, $action) { public static function match($methods, $uri, $action) {
return self::addRoute(array_map('strtoupper', (array)$methods), $uri, $action); return self::addRoute(array_map('strtoupper', (array)$methods), $uri, $action);
} }
public static function any($uri, $action) { public static function any($uri, $action) {
return self::addRoute(array('GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'), $uri, $action); return self::addRoute(['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'], $uri, $action);
} }
public static function get($uri, $action) { public static function get($uri, $action) {
return self::addRoute(['GET', 'HEAD'], $uri, $action); return self::addRoute(['GET', 'HEAD'], $uri, $action);
@ -46,7 +46,7 @@ class Route {
return $route; return $route;
} }
} }
become404Page(); UOJResponse::page404();
} }
protected static function addRoute($methods, $uri, $action) { protected static function addRoute($methods, $uri, $action) {
@ -54,7 +54,7 @@ class Route {
$methods = [$methods]; $methods = [$methods];
} }
$cur = array(); $cur = [];
$cur['methods'] = $methods; $cur['methods'] = $methods;
$cur['uri'] = rtrim($uri, '/'); $cur['uri'] = rtrim($uri, '/');
$cur['action'] = $action; $cur['action'] = $action;
@ -67,21 +67,27 @@ class Route {
return false; return false;
} }
$rep_arr = array(); $rep_arr = [];
foreach (self::$patterns as $name => $pat) { foreach (self::$patterns as $name => $pat) {
$rep_arr['{'.$name.'}'] = "(?P<$name>$pat)"; $rep_arr['{'.$name.'}'] = "(?P<$name>$pat)";
} }
$rep_arr['/'] = '\/'; $rep_arr['/'] = '\/';
$rep_arr['.'] = '\.'; $rep_arr['.'] = '\.';
$matches = array(); $matches = [];
if (isset($route['domain'])) { if (isset($route['domain'])) {
$domain_pat = strtr($route['domain'], $rep_arr); $domain_pat = strtr($route['domain'], $rep_arr);
if (!preg_match('/^'.$domain_pat.'$/', UOJContext::httpHost(), $domain_matches)) { if (!preg_match('/^'.$domain_pat.'$/', UOJContext::requestDomain(), $domain_matches)) {
return false; return false;
} }
$matches = array_merge($matches, $domain_matches); $matches = array_merge($matches, $domain_matches);
} }
if (isset($route['port'])) {
$ports = explode('/', $route['port']);
if (!in_array(UOJContext::requestPort(), $ports)) {
return false;
}
}
$uri_pat = strtr($route['uri'], $rep_arr); $uri_pat = strtr($route['uri'], $rep_arr);
if (!preg_match('/^'.$uri_pat.'$/', rtrim(UOJContext::requestPath(), '/'), $uri_matches)) { if (!preg_match('/^'.$uri_pat.'$/', rtrim(UOJContext::requestPath(), '/'), $uri_matches)) {
@ -95,6 +101,21 @@ class Route {
} }
} }
if (isset($route['protocol'])) {
switch ($route['protocol']) {
case 'http':
if (UOJContext::isUsingHttps()) {
permanentlyRedirectToHTTP();
}
break;
case 'https':
if (!UOJContext::isUsingHttps()) {
permanentlyRedirectToHTTPS();
}
break;
}
}
return true; return true;
} }
} }

View File

@ -5,10 +5,10 @@ class Session {
session_name('UOJSESSID'); session_name('UOJSESSID');
ini_set('session.cookie_path', '/'); ini_set('session.cookie_path', '/');
ini_set('session.cookie_domain', UOJContext::cookieDomain()); ini_set('session.cookie_domain', UOJContext::cookieDomain());
session_start(); session_start();
register_shutdown_function(function() { register_shutdown_function(function () {
if (empty($_SESSION)) { if (empty($_SESSION)) {
session_unset(); session_unset();
session_destroy(); session_destroy();

View File

@ -0,0 +1,65 @@
<?php
class StrictFileReader {
private $f;
private $buf = '', $off = 0;
public function __construct($file_name) {
$this->f = fopen($file_name, 'r');
}
public function failed() {
return $this->f === false;
}
public function readChar() {
if (isset($this->buf[$this->off])) {
return $this->buf[$this->off++];
}
return fgetc($this->f);
}
public function unreadChar($c) {
$this->buf .= $c;
if ($this->off > 1000) {
$this->buf = substr($this->buf, $this->off);
$this->off = 0;
}
}
public function readString() {
$str = '';
while (true) {
$c = $this->readChar();
if ($c === false) {
break;
} elseif ($c === " " || $c === "\n" || $c === "\r") {
$this->unreadChar($c);
break;
} else {
$str .= $c;
}
}
return $str;
}
public function ignoreWhite() {
while (true) {
$c = $this->readChar();
if ($c === false) {
break;
} elseif ($c === " " || $c === "\n" || $c === "\r") {
continue;
} else {
$this->unreadChar($c);
break;
}
}
}
public function eof() {
return feof($this->f);
}
public function close() {
fclose($this->f);
}
}

View File

@ -0,0 +1,55 @@
<?php
trait UOJArticleTrait {
static string $table_for_content;
static string $key_for_content;
static array $fields_for_content;
static string $table_for_tags;
static string $key_for_tags;
public ?array $tags = null;
public ?array $content = null;
public function queryContent() {
if ($this->content === null) {
$this->content = DB::selectFirst([
"select", DB::fields(static::$fields_for_content), "from", static::$table_for_content,
"where", [static::$key_for_content => $this->info['id']]
]);
}
return $this->content;
}
public function queryTags() {
if ($this->tags === null) {
$res = DB::selectAll([
"select tag from", static::$table_for_tags,
"where", [static::$key_for_tags => $this->info['id']],
"order by id"
]);
$this->tags = [];
foreach ($res as $row) {
$this->tags[] = $row['tag'];
}
}
return $this->tags;
}
public function updateTags(array $tags) {
if ($tags !== $this->queryTags()) {
DB::delete([
"delete from", static::$table_for_tags,
"where", [static::$key_for_tags => $this->info['id']],
]);
if ($tags) {
$tuples = [];
foreach ($tags as $tag) {
$tuples[] = [$this->info['id'], $tag];
}
DB::insert([
"insert into", static::$table_for_tags,
DB::bracketed_fields([static::$key_for_tags, 'tag']),
"values", DB::tuples($tuples)
]);
}
}
}
}

200
web/app/models/UOJBlog.php Normal file
View File

@ -0,0 +1,200 @@
<?php
class UOJBlog {
use UOJDataTrait;
use UOJArticleTrait;
public static function query($id) {
if (!isset($id) || !validateUInt($id)) {
return null;
}
$info = DB::selectFirst([
"select id, title, post_time, active_time, poster, zan, is_hidden, type from blogs",
"where", ['id' => $id]
]);
if (!$info) {
return null;
}
return new UOJBlog($info);
}
public function __construct($info) {
$this->info = $info;
}
/**
* Check if the blog belongs to the current user blog
*/
public function belongsToUserBlog() {
return UOJContext::type() == 'blog' && UOJUserBlog::id() === $this->info['poster'];
}
public function userCanView(array $user = null) {
return !$this->info['is_hidden'] || $this->userCanManage($user);
}
public function userCanManage(array $user = null) {
return UOJUserBlog::userCanManage($user, $this->info['poster']);
}
public function isTypeB() {
return $this->info['type'] === 'B';
}
public function isTypeS() {
return $this->info['type'] === 'S';
}
public function getTitle(array $cfg = []) {
$title = $this->info['title'];
return $title;
}
public function getBlogUri($where = '') {
return HTML::blog_url($this->info['poster'], "/post/{$this->info['id']}{$where}");
}
public function getSlideUri($where = '') {
return HTML::blog_url($this->info['poster'], "/slide/{$this->info['id']}{$where}");
}
public function getUriForWrite() {
return $this->isTypeB() ? $this->getBlogUri('/write') : $this->getSlideUri('/write');
}
public function getLink(array $cfg = []) {
// title has been escaped by the blog editor
$link = '';
if (!empty($cfg['show_level'])) {
$level_str = $this->getLevelString();
if ($level_str !== '') {
$link .= "{$level_str} ";
}
}
$link .= '<a href="'. $this->getBlogUri() .'">'.$this->getTitle($cfg).'</a>';
if (!empty($cfg['show_new_tag'])) {
if ($this->isNew()) {
$link .= '<sup style="color:red">&nbsp;new</sup>';
}
}
return $link;
}
/**
* Return true if the blog is marked as "new".
* Call this function only if info['is_new'] has been filled by the corresponding value in the table important_blogs.
*/
public function isNew() {
return (time() - strtotime($this->info['post_time'])) / 3600 / 24 <= 7;
}
/**
* Return the string that shows the importance level of this blog.
* Call this function only if info['level'] has been filled by the corresponding value in the table important_blogs.
*/
public function getLevelString() {
$level = $this->info['level'];
switch ($level) {
case 1:
return '<span style="color:red">[三级置顶]</span>';
case 2:
return '<span style="color:red">[二级置顶]</span>';
case 3:
return '<span style="color:red">[一级置顶]</span>';
default: // such as 0
return '';
}
}
public function queryNewestComment() {
return DB::selectFirst([
"select * from blogs_comments",
"where", [
'blog_id' => $this->info['id'],
'is_hidden' => false
],
"order by post_time desc",
DB::limit(1)
]);
}
public function updateActiveTime() {
$active_time = $this->info['post_time'];
$newest = $this->queryNewestComment();
if ($newest) {
$active_time = $newest['post_time'];
}
DB::update([
"update blogs",
"set", ['active_time' => $active_time],
"where", ['id' => $this->info['id']]
]);
}
public static function deleteByID($id) {
$id = (int)$id;
DB::delete([
"delete from click_zans",
"where", [
'type' => 'B',
'target_id' => $id
]
]);
DB::delete([
"delete from click_zans",
"where", [
'type' => 'BC',
[
"target_id", "in", DB::rawbracket([
"select id from blogs_comments",
"where", ["blog_id" => $id]
])
]
]
]);
DB::delete([
"delete from blogs",
"where", ['id' => $id]
]);
DB::delete([
"delete from blogs_comments",
"where", ['blog_id' => $id]
]);
DB::delete([
"delete from important_blogs",
"where", ['blog_id' => $id]
]);
DB::delete([
"delete from blogs_tags",
"where", ['blog_id' => $id]
]);
DB::delete([
"delete from problems_solutions",
"where", ['blog_id' => $id]
]);
}
public function delete() {
self::deleteByID($this->info['id']);
}
public function echoView(array $cfg = []) {
// load tags and content into cache
$this->queryContent();
$this->queryTags();
$cfg += [
'blog' => $this,
'show_title_only' => false,
'is_preview' => false
];
uojIncludeView('blog-preview', $cfg);
}
}
UOJBlog::$table_for_content = 'blogs';
UOJBlog::$key_for_content = 'id';
UOJBlog::$fields_for_content = ['content', 'content_md'];
UOJBlog::$table_for_tags = 'blogs_tags';
UOJBlog::$key_for_tags = 'blog_id';

View File

@ -0,0 +1,39 @@
<?php
class UOJBlogComment {
use UOJDataTrait;
public static function query($id) {
if (!isset($id) || !validateUInt($id)) {
return null;
}
$info = DB::selectFirst([
"select * from blogs_comments",
"where", ['id' => $id]
]);
if (!$info) {
return null;
}
return new UOJBlogComment($info);
}
public function __construct($info) {
$this->info = $info;
}
public function hide($reason) {
DB::update([
"update blogs_comments",
"set", [
'is_hidden' => ($reason !== ''),
'reason_to_hide' => $reason
],
"where", ['id' => $this->info['id']]
]);
$blog = UOJBlog::query($this->info['blog_id']);
if ($blog) {
$blog->updateActiveTime($blog);
}
}
}

Some files were not shown because too many files have changed in this diff Show More