├── web ├── .gitignore └── src │ ├── main │ ├── webapp │ │ ├── assets │ │ │ ├── img │ │ │ │ ├── logo.jpg │ │ │ │ ├── logo.png │ │ │ │ ├── avatar.jpg │ │ │ │ ├── error.png │ │ │ │ ├── favicon.ico │ │ │ │ ├── favicon.png │ │ │ │ ├── loading.gif │ │ │ │ ├── wmd-draft.png │ │ │ │ ├── browsers │ │ │ │ │ ├── ie.jpg │ │ │ │ │ ├── opera.jpg │ │ │ │ │ ├── chrome.jpg │ │ │ │ │ ├── firefox.jpg │ │ │ │ │ └── safari.jpg │ │ │ │ ├── discussion.png │ │ │ │ ├── logo-light.png │ │ │ │ ├── switch-mask.png │ │ │ │ ├── sliders │ │ │ │ │ ├── slide-01.jpg │ │ │ │ │ ├── slide-02.jpg │ │ │ │ │ └── slide-03.jpg │ │ │ │ ├── upgrade-browser.png │ │ │ │ ├── glyphicons-halflings.png │ │ │ │ ├── police-badge-of-china.png │ │ │ │ └── glyphicons-halflings-white.png │ │ │ ├── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── Flat-UI-Icons.eot │ │ │ │ ├── Flat-UI-Icons.ttf │ │ │ │ ├── Flat-UI-Icons.woff │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ └── fontawesome-webfont.woff │ │ │ ├── css │ │ │ │ ├── misc │ │ │ │ │ ├── error.css │ │ │ │ │ └── about.css │ │ │ │ ├── discussion │ │ │ │ │ └── new-thread.css │ │ │ │ ├── administration │ │ │ │ │ ├── new-user.css │ │ │ │ │ ├── general-settings.css │ │ │ │ │ ├── language-settings.css │ │ │ │ │ ├── edit-submission.css │ │ │ │ │ ├── all-users.css │ │ │ │ │ ├── all-problems.css │ │ │ │ │ ├── edit-user.css │ │ │ │ │ ├── problem-categories.css │ │ │ │ │ ├── all-submissions.css │ │ │ │ │ └── dashboard.css │ │ │ │ ├── contests │ │ │ │ │ ├── leaderboard.css │ │ │ │ │ └── contest.css │ │ │ │ ├── accounts │ │ │ │ │ ├── login.css │ │ │ │ │ ├── reset-password.css │ │ │ │ │ └── register.css │ │ │ │ └── highlight.min.css │ │ │ ├── mode │ │ │ │ ├── pascal.min.js │ │ │ │ └── go.min.js │ │ │ └── js │ │ │ │ └── site.js │ │ ├── index.jsp │ │ └── WEB-INF │ │ │ ├── views │ │ │ ├── administration │ │ │ │ └── include │ │ │ │ │ ├── header.jsp │ │ │ │ │ └── footer-script.jsp │ │ │ └── include │ │ │ │ └── footer.jsp │ │ │ └── web.xml │ ├── resources │ │ ├── log4j2.xml │ │ ├── voj.properties │ │ ├── mappers │ │ │ ├── JudgeResultMapper.xml │ │ │ ├── UserGroupMapper.xml │ │ │ ├── EmailValidationMapper.xml │ │ │ ├── OptionMapper.xml │ │ │ ├── CheckpointMapper.xml │ │ │ ├── UserMetaMapper.xml │ │ │ ├── LanguageMapper.xml │ │ │ ├── DiscussionTopicMapper.xml │ │ │ ├── BulletinBoardMessageMapper.xml │ │ │ ├── ContestSubmissionMapper.xml │ │ │ ├── ContestMapper.xml │ │ │ └── DiscussionReplyMapper.xml │ │ └── mails │ │ │ └── reset-password.ftl │ └── java │ │ └── org │ │ └── verwandlung │ │ └── voj │ │ └── web │ │ ├── exception │ │ └── ResourceNotFoundException.java │ │ ├── util │ │ ├── HttpRequestParser.java │ │ ├── DateUtils.java │ │ ├── HtmlTextFilter.java │ │ ├── SlugifyUtils.java │ │ ├── DigestUtils.java │ │ ├── HttpSessionParser.java │ │ └── LocaleUtils.java │ │ ├── mapper │ │ ├── JudgeResultMapper.java │ │ ├── EmailValidationMapper.java │ │ ├── UserGroupMapper.java │ │ ├── OptionMapper.java │ │ ├── CheckpointMapper.java │ │ ├── ContestMapper.java │ │ ├── DiscussionTopicMapper.java │ │ ├── LanguageMapper.java │ │ └── DiscussionReplyMapper.java │ │ ├── messenger │ │ └── MessageSender.java │ │ ├── model │ │ └── ContestSubmission.java │ │ └── service │ │ └── BulletinBoardService.java │ └── test │ ├── resources │ ├── log4j2-test.xml │ └── voj-test.properties │ └── java │ └── org │ └── verwandlung │ └── voj │ └── web │ └── util │ ├── MessageReceiverTest.java │ ├── SlugifyUtilsTest.java │ ├── HtmlTextFilterTest.java │ ├── MessageSenderTest.java │ ├── OffensiveWordFilterTest.java │ └── DigestUtilsTest.java ├── .lgtm.yml ├── docker ├── web │ └── supervisord.conf ├── README.md └── judger │ └── Dockerfile ├── .gitignore ├── judger ├── src │ ├── test │ │ ├── resources │ │ │ ├── log4j2-test.xml │ │ │ ├── voj-test-linux.properties │ │ │ └── voj-test-windows.properties │ │ └── java │ │ │ └── org │ │ │ └── verwandlung │ │ │ └── voj │ │ │ └── judger │ │ │ └── mapper │ │ │ ├── UserMapperTest.java │ │ │ ├── ProblemMapperTest.java │ │ │ ├── SubmissionMapperTest.java │ │ │ └── UserGroupMapperTest.java │ └── main │ │ ├── cpp │ │ ├── org_verwandlung_voj_judger_core_Runner.h │ │ └── org_verwandlung_voj_jni_library.h │ │ ├── resources │ │ ├── voj.properties │ │ └── log4j2.xml │ │ └── java │ │ └── org │ │ └── verwandlung │ │ └── voj │ │ └── judger │ │ ├── exception │ │ ├── CreateDirectoryException.java │ │ └── IllgealSubmissionException.java │ │ ├── messenger │ │ └── MessageSender.java │ │ └── mapper │ │ ├── UserGroupMapper.java │ │ └── CheckpointMapper.java └── Makefile ├── .github └── workflows │ ├── docker.yml │ └── sonarqube.yml ├── .travis.yml ├── .circleci └── config.yml └── appveyor.yml /web/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/logo.jpg -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/logo.png -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/avatar.jpg -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/error.png -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/favicon.ico -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/favicon.png -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/loading.gif -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/wmd-draft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/wmd-draft.png -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/browsers/ie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/browsers/ie.jpg -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/discussion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/discussion.png -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/logo-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/logo-light.png -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/switch-mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/switch-mask.png -------------------------------------------------------------------------------- /web/src/main/webapp/assets/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/browsers/opera.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/browsers/opera.jpg -------------------------------------------------------------------------------- /web/src/main/webapp/assets/fonts/Flat-UI-Icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/fonts/Flat-UI-Icons.eot -------------------------------------------------------------------------------- /web/src/main/webapp/assets/fonts/Flat-UI-Icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/fonts/Flat-UI-Icons.ttf -------------------------------------------------------------------------------- /web/src/main/webapp/assets/fonts/Flat-UI-Icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/fonts/Flat-UI-Icons.woff -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/browsers/chrome.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/browsers/chrome.jpg -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/browsers/firefox.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/browsers/firefox.jpg -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/browsers/safari.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/browsers/safari.jpg -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/sliders/slide-01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/sliders/slide-01.jpg -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/sliders/slide-02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/sliders/slide-02.jpg -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/sliders/slide-03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/sliders/slide-03.jpg -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/upgrade-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/upgrade-browser.png -------------------------------------------------------------------------------- /web/src/main/webapp/assets/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /web/src/main/webapp/assets/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/police-badge-of-china.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/police-badge-of-china.png -------------------------------------------------------------------------------- /web/src/main/webapp/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /web/src/main/webapp/assets/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hzxie/voj/HEAD/web/src/main/webapp/assets/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /.lgtm.yml: -------------------------------------------------------------------------------- 1 | extraction: 2 | java: 3 | index: 4 | build_command: 5 | - "mvn package -DskipTests -f web/pom.xml" 6 | - "mvn package -DskipTests -f judger/pom.xml" 7 | 8 | -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/misc/error.css: -------------------------------------------------------------------------------- 1 | div#content div#error-message { 2 | padding-top: 180px; 3 | } 4 | 5 | @media only screen and (max-width: 767px) { 6 | div#content div#error-message { 7 | padding-top: 0; 8 | } 9 | } -------------------------------------------------------------------------------- /docker/web/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | 4 | [program:mysqld] 5 | command=/etc/init.d/mariadb start 6 | 7 | [program:activemq] 8 | command=/opt/activemq/bin/activemq start 9 | 10 | [program:tomcat] 11 | command=bash -c 'sleep 15 && /opt/tomcat/bin/startup.sh' 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | bin/ 3 | logs/ 4 | cache/ 5 | 6 | # Maven 7 | target/ 8 | pom.xml.tag 9 | pom.xml.releaseBackup 10 | pom.xml.versionsBackup 11 | pom.xml.next 12 | release.properties 13 | dependency-reduced-pom.xml 14 | 15 | # Eclipse 16 | .classpath 17 | .project 18 | .settings/ 19 | 20 | # IntelliJ IDEA 21 | *.iml 22 | .idea/ 23 | META-INF/ 24 | 25 | # File Generated by Mac 26 | .DS_Store 27 | -------------------------------------------------------------------------------- /web/src/test/resources/log4j2-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [%p] %d [%t] %c - %m%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /judger/src/test/resources/log4j2-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [%p] %d [%t] %c - %m%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /web/src/test/resources/voj-test.properties: -------------------------------------------------------------------------------- 1 | # Database Configuration 2 | jdbc.driverClassName = com.mysql.cj.jdbc.Driver 3 | jdbc.url = jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8&serverTimezone=UTC 4 | jdbc.username = root 5 | jdbc.password = 6 | jdbc.initialSize = 1 7 | jdbc.maxActive = 1 8 | jdbc.minIdle = 0 9 | jdbc.maxIdle = 1 10 | jdbc.maxWait = 1800 11 | jdbc.timeBetweenEvictionRunsMillis = 1800 12 | jdbc.minEvictableIdleTimeMillis = 1800 13 | jdbc.removeAbandoned = true 14 | jdbc.removeAbandonedTimeout = 1800 15 | 16 | # Mail Service Configuration 17 | mail.host = 18 | mail.username = 19 | mail.password = 20 | mail.senderMail = 21 | mail.senderName = 22 | 23 | # Message Service Configuration 24 | jms.broker.url = vm://localhost?broker.persistent=false 25 | 26 | # Web Service Configuration 27 | url.base = -------------------------------------------------------------------------------- /judger/src/main/cpp/org_verwandlung_voj_judger_core_Runner.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class org_verwandlung_voj_judger_core_Runner */ 4 | 5 | #ifndef _Included_org_verwandlung_voj_judger_core_Runner 6 | #define _Included_org_verwandlung_voj_judger_core_Runner 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: org_verwandlung_voj_judger_core_Runner 12 | * Method: getRuntimeResult 13 | * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II)Ljava/util/Map; 14 | */ 15 | JNIEXPORT jobject JNICALL Java_org_verwandlung_voj_judger_core_Runner_getRuntimeResult 16 | (JNIEnv *, jobject, jstring, jstring, jstring, jstring, jstring, jint, jint); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /web/src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> 2 | <%@ taglib prefix="c" uri="jakarta.tags.core" %> 3 | 4 | 5 | 6 | 7 | ${WebsiteName} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /judger/src/main/resources/voj.properties: -------------------------------------------------------------------------------- 1 | # Database Configuration 2 | jdbc.driverClassName = com.mysql.cj.jdbc.Driver 3 | jdbc.url = jdbc:mysql://localhost:3306/voj?characterEncoding=UTF-8&serverTimezone=UTC 4 | jdbc.username = root 5 | jdbc.password = 6 | jdbc.initialSize = 5 7 | jdbc.maxActive = 30 8 | jdbc.minIdle = 3 9 | jdbc.maxIdle = 10 10 | jdbc.maxWait = 30000 11 | jdbc.timeBetweenEvictionRunsMillis = 60000 12 | jdbc.minEvictableIdleTimeMillis = 25200000 13 | jdbc.removeAbandoned = true 14 | jdbc.removeAbandonedTimeout = 1800 15 | 16 | # Message Service Configuration 17 | jms.broker.url = tcp://localhost:61616 18 | 19 | # Authentication for voj 20 | judger.username = voj@judger 21 | judger.password = zjhzxhz 22 | judger.description = 23 | 24 | # Working Directory 25 | judger.workDir = /tmp 26 | judger.checkpointDir = /tmp/voj-testpoints 27 | 28 | # System User with Lower Privileges 29 | system.username = hzxie 30 | system.password = @AppVeyor -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/discussion/new-thread.css: -------------------------------------------------------------------------------- 1 | div#discussion-thread { 2 | margin-bottom: 60px; 3 | } 4 | 5 | div#discussion-thread div.header { 6 | border-bottom: 1px solid #dedede; 7 | } 8 | 9 | div#discussion-thread select, 10 | div#discussion-thread input[type="text"] { 11 | height: auto; 12 | -webkit-box-sizing:border-box; 13 | -moz-box-sizing: border-box; 14 | box-sizing: border-box; 15 | } 16 | 17 | div#discussion-thread div.body div.markdown-editor div.wmd-button-bar, 18 | div#discussion-thread div.body div.markdown-editor textarea.wmd-input { 19 | border-width: 1px; 20 | } 21 | 22 | div#discussion-thread div.body div.section { 23 | border-bottom: 1px solid #dedede; 24 | font-size: 13px; 25 | padding: 10px 0 10px; 26 | } 27 | 28 | div#discussion-thread div.body div.section:last-child { 29 | border-bottom: none; 30 | } 31 | 32 | div#discussion-thread div.body div.section p { 33 | padding-left: 15px; 34 | } 35 | -------------------------------------------------------------------------------- /judger/Makefile: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | CFLAGS=-c -std=c++11 -Wall -fPIC -I "${JAVA_HOME}/include" -I "${JAVA_HOME}/include/linux" 3 | LDFLAGS=-fPIC -shared 4 | EXTFLAGS= 5 | 6 | ifeq ($(OS), Windows_NT) 7 | SOURCES_DIR=src/main/cpp/windows 8 | OBJECTS_DIR=target/cpp 9 | EXECUTABLE=target/classes/JudgerCore.dll 10 | EXTFLAGS=-luserenv -lpsapi 11 | else 12 | UNAME_S := $(shell uname -s) 13 | ifeq ($(UNAME_S), Linux) 14 | SOURCES_DIR=src/main/cpp/unix 15 | OBJECTS_DIR=target/cpp 16 | EXECUTABLE=target/classes/libJudgerCore.so 17 | EXTFLAGS=-lpthread -lrt 18 | endif 19 | endif 20 | 21 | SOURCES=$(wildcard $(SOURCES_DIR)/*.cpp) 22 | OBJECTS=$(SOURCES:$(SOURCES_DIR)/%.cpp=$(OBJECTS_DIR)/%.o) 23 | 24 | all: $(EXECUTABLE) 25 | 26 | $(EXECUTABLE): $(OBJECTS) 27 | $(CC) $(LDFLAGS) $(OBJECTS) $(EXTFLAGS) -o $@ 28 | 29 | $(OBJECTS): $(SOURCES) 30 | mkdir -p $(OBJECTS_DIR) 31 | $(CC) $(CFLAGS) $< -o $@ 32 | 33 | clean: 34 | rm -rf $(OBJECTS_DIR) $(EXECUTABLE) 35 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # Docker for Verwandlung Online Judge 2 | 3 | ## Build Docker Image for Web Application 4 | 5 | ``` 6 | cd web 7 | docker build -t "zjhzxhz/voj.web" . 8 | ``` 9 | 10 | You can also pull it from [Docker Hub](https://hub.docker.com/r/zjhzxhz/voj.web/). 11 | 12 | ``` 13 | docker pull zjhzxhz/voj.web 14 | ``` 15 | 16 | ## Run Web Application 17 | 18 | ``` 19 | docker run -d --name voj.web -p 8080:8080 zjhzxhz/voj.web 20 | ``` 21 | 22 | The web application is available at [http://localhost:8080/voj](http://localhost:8080/voj). 23 | 24 | ## Build Docker Image for Judger Application 25 | 26 | ``` 27 | cd judger 28 | docker build -t "zjhzxhz/voj.judger" . 29 | ``` 30 | 31 | You can also pull it from [Docker Hub](https://hub.docker.com/r/zjhzxhz/voj.judger/). 32 | 33 | ``` 34 | docker pull zjhzxhz/voj.judger 35 | ``` 36 | 37 | ## Run Judger Application 38 | 39 | ``` 40 | docker run -d --name voj.judger --link voj.web zjhzxhz/voj.judger 41 | ``` 42 | -------------------------------------------------------------------------------- /judger/src/test/resources/voj-test-linux.properties: -------------------------------------------------------------------------------- 1 | # Database Configuration 2 | jdbc.driverClassName = com.mysql.cj.jdbc.Driver 3 | jdbc.url = jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8&serverTimezone=UTC 4 | jdbc.username = root 5 | jdbc.password = 6 | jdbc.initialSize = 1 7 | jdbc.maxActive = 1 8 | jdbc.minIdle = 0 9 | jdbc.maxIdle = 1 10 | jdbc.maxWait = 1800 11 | jdbc.timeBetweenEvictionRunsMillis = 1800 12 | jdbc.minEvictableIdleTimeMillis = 1800 13 | jdbc.removeAbandoned = true 14 | jdbc.removeAbandonedTimeout = 1800 15 | 16 | # Message Service Configuration 17 | jms.broker.url = vm://localhost?broker.persistent=false 18 | 19 | # Authentication for voj 20 | judger.username = 21 | judger.password = 22 | judger.description = 23 | 24 | # Working Directory 25 | judger.workDir = /tmp 26 | judger.checkpointDir = /tmp/testpoints 27 | 28 | # System User with Lower Privileges 29 | system.username = appveyor 30 | system.password = Fx8eHPNesxhpmdru -------------------------------------------------------------------------------- /judger/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | logs/voj-judger.log 6 | logs/voj-judger-%d{yyyy-MM-dd}.log 7 | [%p] %d [%t] %c - %m%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /judger/src/test/resources/voj-test-windows.properties: -------------------------------------------------------------------------------- 1 | # Database Configuration 2 | jdbc.driverClassName = com.mysql.cj.jdbc.Driver 3 | jdbc.url = jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8&serverTimezone=UTC 4 | jdbc.username = root 5 | jdbc.password = 6 | jdbc.initialSize = 1 7 | jdbc.maxActive = 1 8 | jdbc.minIdle = 0 9 | jdbc.maxIdle = 1 10 | jdbc.maxWait = 1800 11 | jdbc.timeBetweenEvictionRunsMillis = 1800 12 | jdbc.minEvictableIdleTimeMillis = 1800 13 | jdbc.removeAbandoned = true 14 | jdbc.removeAbandonedTimeout = 1800 15 | 16 | # Message Service Configuration 17 | jms.broker.url = vm://localhost?broker.persistent=false 18 | 19 | # Authentication for voj 20 | judger.username = 21 | judger.password = 22 | judger.description = 23 | 24 | # Working Directory 25 | judger.workDir = C:/Windows/Temp 26 | judger.checkpointDir = C:/Windows/Temp/testpoints 27 | 28 | # System User with Lower Privileges 29 | system.username = appveyor 30 | system.password = Fx8eHPNesxhpmdru -------------------------------------------------------------------------------- /web/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ${sys:catalina.home}/logs/voj-web.log 6 | ${sys:catalina.home}/logs/voj-web-%d{yyyy-MM-dd}.log 7 | [%p] %d [%t] %c - %m%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/administration/new-user.css: -------------------------------------------------------------------------------- 1 | form#profile-form { 2 | max-width: 750px; 3 | } 4 | 5 | form#profile-form label { 6 | font-weight: bold; 7 | } 8 | 9 | form#profile-form label > a { 10 | font-weight: normal; 11 | } 12 | 13 | form#profile-form input[type=text], 14 | form#profile-form textarea { 15 | resize: none; 16 | width: 100%; 17 | } 18 | 19 | /* Smaller than standard 960 (devices and browsers) */ 20 | @media only screen and (max-width: 959px) { 21 | } 22 | 23 | /* Tablet Portrait size to standard 960 (devices and browsers) */ 24 | @media only screen and (min-width: 768px) and (max-width: 959px) { 25 | } 26 | 27 | /* All Mobile Sizes (devices and browser) */ 28 | @media only screen and (max-width: 767px) { 29 | } 30 | 31 | /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ 32 | @media only screen and (min-width: 480px) and (max-width: 767px) { 33 | } 34 | 35 | /* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ 36 | @media only screen and (max-width: 479px) { 37 | } -------------------------------------------------------------------------------- /web/src/main/resources/voj.properties: -------------------------------------------------------------------------------- 1 | # The following line is used for #include attack 2 | #if false 3 | 4 | # Database Configuration 5 | jdbc.driverClassName = com.mysql.cj.jdbc.Driver 6 | jdbc.url = jdbc:mysql://localhost:3306/voj?characterEncoding=UTF-8&autoReconnect=true&serverTimezone=UTC 7 | jdbc.username = root 8 | jdbc.password = 9 | jdbc.initialSize = 5 10 | jdbc.maxActive = 30 11 | jdbc.minIdle = 3 12 | jdbc.maxIdle = 10 13 | jdbc.maxWait = 30000 14 | jdbc.timeBetweenEvictionRunsMillis = 60000 15 | jdbc.minEvictableIdleTimeMillis = 25200000 16 | jdbc.removeAbandoned = true 17 | jdbc.removeAbandonedTimeout = 1800 18 | 19 | # Mail Service Configuration 20 | mail.host = 21 | mail.username = 22 | mail.password = 23 | mail.senderMail = noreply@example.com 24 | mail.senderName = Verwandlung Online Judge 25 | 26 | # Message Service Configuration 27 | jms.broker.url = tcp://localhost:61616 28 | 29 | # Web Service Configuration 30 | url.base = http://localhost:8080/voj 31 | url.cdn = http://localhost:8080/voj/assets 32 | 33 | # Version 34 | build.version = 226111d 35 | product.version = 0.2.0 36 | 37 | #endif -------------------------------------------------------------------------------- /web/src/main/resources/mappers/JudgeResultMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 22 | 30 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push Docker Image 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build-and-push: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v4 15 | 16 | - name: Log in to DockerHub 17 | uses: docker/login-action@v3 18 | with: 19 | username: ${{ secrets.DOCKER_USERNAME }} 20 | password: ${{ secrets.DOCKER_PASSWORD }} 21 | 22 | - name: Patch Dockerfile for GitHub Actions 23 | run: sed -i.bak 's|COPY supervisord.conf|COPY docker/web/supervisord.conf|' docker/web/Dockerfile 24 | 25 | - name: Build and Push Web Image 26 | run: | 27 | docker build -t ${{ secrets.DOCKER_USERNAME }}/voj.web:latest -f docker/web/Dockerfile . 28 | docker push ${{ secrets.DOCKER_USERNAME }}/voj.web:latest 29 | 30 | - name: Build and Push Judger Image 31 | run: | 32 | docker build -t ${{ secrets.DOCKER_USERNAME }}/voj.judger:latest -f docker/judger/Dockerfile . 33 | docker push ${{ secrets.DOCKER_USERNAME }}/voj.judger:latest 34 | 35 | -------------------------------------------------------------------------------- /web/src/main/resources/mappers/UserGroupMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 29 | 37 | -------------------------------------------------------------------------------- /web/src/main/resources/mappers/EmailValidationMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 22 | 25 | INSERT INTO voj_email_validation (email, token, expire_time) 26 | VALUES (#{email}, #{token}, #{expireTime}) 27 | 28 | 31 | DELETE FROM voj_email_validation 32 | WHERE email = #{email} 33 | 34 | -------------------------------------------------------------------------------- /web/src/main/webapp/WEB-INF/views/administration/include/header.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 2 | -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/contests/leaderboard.css: -------------------------------------------------------------------------------- 1 | div#content div.leaderboard { 2 | -webkit-box-shadow: 0 0 5px rgba(0,0,0,0.1); 3 | -moz-box-shadow: 0 0 5px rgba(0,0,0,0.1); 4 | box-shadow: 0 0 5px rgba(0,0,0,0.1); 5 | } 6 | 7 | div#content div.leaderboard div.header { 8 | background-color: #16a085; 9 | color: #fff; 10 | padding: 10px; 11 | } 12 | 13 | div#content div.leaderboard div.body { 14 | background-color: #fff; 15 | position: relative; 16 | } 17 | 18 | div#content div.leaderboard div.body thead { 19 | background: #fff; 20 | top: 0; 21 | } 22 | 23 | div#content div.leaderboard div.body th.rank, 24 | div#content div.leaderboard div.body td.rank { 25 | border-right: 1px solid #ddd; 26 | text-align: center; 27 | width: 40px; 28 | } 29 | 30 | div#content div.leaderboard div.body th.contestant, 31 | div#content div.leaderboard div.body td.contestant { 32 | width: 80px; 33 | } 34 | 35 | div#content div.leaderboard div.body th.score, 36 | div#content div.leaderboard div.body td.score { 37 | text-align: center; 38 | width: 60px; 39 | } 40 | 41 | div#content div.leaderboard div.body th.time, 42 | div#content div.leaderboard div.body td.time { 43 | border-right: 1px solid #ddd; 44 | text-align: center; 45 | width: 80px; 46 | } 47 | 48 | div#content div.leaderboard div.body th.submission, 49 | div#content div.leaderboard div.body td.submission { 50 | border-right: 1px solid #ddd; 51 | text-align: center; 52 | } 53 | -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/accounts/login.css: -------------------------------------------------------------------------------- 1 | div#header { 2 | border-top: 4px solid #1abc9c; 3 | } 4 | 5 | div#content div#login { 6 | margin: 20px auto 20px; 7 | width: 400px; 8 | } 9 | 10 | div#content div#login h2 { 11 | color: #1abc9c; 12 | } 13 | 14 | div#content div#login div.alert { 15 | margin-top: 20px; 16 | font-size: 13px; 17 | } 18 | 19 | div#content div#login form#login-form { 20 | background: #fff; 21 | border: 1px solid #dedede; 22 | color: #666; 23 | font-size: 12px; 24 | margin: 20px auto; 25 | padding: 40px; 26 | -webkit-border-radius: 4px; 27 | -moz-border-radius: 4px; 28 | border-radius: 4px; 29 | } 30 | 31 | div#content div#login form#login-form label { 32 | font-size: 12px; 33 | } 34 | 35 | /* Smaller than standard 960 (devices and browsers) */ 36 | /* Tablet Portrait size to standard 960 (devices and browsers) */ 37 | @media only screen and (min-width: 768px) and (max-width: 959px) { 38 | } 39 | 40 | /* All Mobile Sizes (devices and browser) */ 41 | /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ 42 | @media only screen and (min-width: 480px) and (max-width: 767px) { 43 | } 44 | 45 | /* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ 46 | @media only screen and (max-width: 479px) { 47 | div#content div#login { 48 | width: 300px; 49 | } 50 | 51 | div#content div#login form#login-form { 52 | padding: 20px; 53 | } 54 | } -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/administration/general-settings.css: -------------------------------------------------------------------------------- 1 | form#option-form { 2 | max-width: 750px; 3 | } 4 | 5 | form#option-form label { 6 | font-weight: bold; 7 | } 8 | 9 | form#option-form label > a { 10 | font-weight: normal; 11 | } 12 | 13 | form#option-form input[type=text], 14 | form#option-form textarea { 15 | resize: none; 16 | width: 100%; 17 | } 18 | 19 | form#option-form div.switch-container div.span8 { 20 | padding-top: 5px; 21 | } 22 | 23 | form#option-form div.switch-container div.span4 { 24 | text-align: right; 25 | } 26 | 27 | form#option-form div.tagsinput { 28 | border: 2px solid #bdc3c7; 29 | } 30 | 31 | form#option-form div.tagsinput:focus { 32 | border: 2px solid #1abc9c; 33 | } 34 | 35 | /* Smaller than standard 960 (devices and browsers) */ 36 | @media only screen and (max-width: 959px) { 37 | } 38 | 39 | /* Tablet Portrait size to standard 960 (devices and browsers) */ 40 | @media only screen and (min-width: 768px) and (max-width: 959px) { 41 | } 42 | 43 | /* All Mobile Sizes (devices and browser) */ 44 | @media only screen and (max-width: 767px) { 45 | form#option-form div.switch-container div.span4 { 46 | text-align: left; 47 | } 48 | } 49 | 50 | /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ 51 | @media only screen and (min-width: 480px) and (max-width: 767px) { 52 | } 53 | 54 | /* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ 55 | @media only screen and (max-width: 479px) { 56 | } -------------------------------------------------------------------------------- /web/src/main/resources/mappers/OptionMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 28 | 36 | 39 | UPDATE voj_options 40 | SET option_name = #{optionName}, option_value = #{optionValue}, is_autoload = #{isAutoload} 41 | WHERE option_id = #{optionId} 42 | 43 | -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/accounts/reset-password.css: -------------------------------------------------------------------------------- 1 | div#header { 2 | border-top: 4px solid #1abc9c; 3 | } 4 | 5 | div#content div#reset-password { 6 | margin: 20px auto 20px; 7 | width: 400px; 8 | } 9 | 10 | div#content div#reset-password h2 { 11 | color: #1abc9c; 12 | } 13 | 14 | div#content div#reset-password div.alert { 15 | margin-top: 20px; 16 | font-size: 13px; 17 | } 18 | 19 | div#content div#reset-password form#reset-password-form { 20 | background: #fff; 21 | border: 1px solid #dedede; 22 | color: #666; 23 | font-size: 12px; 24 | margin: 20px auto; 25 | padding: 40px; 26 | -webkit-border-radius: 4px; 27 | -moz-border-radius: 4px; 28 | border-radius: 4px; 29 | } 30 | 31 | div#content div#reset-password form#reset-password-form label { 32 | font-size: 12px; 33 | } 34 | 35 | /* Smaller than standard 960 (devices and browsers) */ 36 | /* Tablet Portrait size to standard 960 (devices and browsers) */ 37 | @media only screen and (min-width: 768px) and (max-width: 959px) { 38 | } 39 | 40 | /* All Mobile Sizes (devices and browser) */ 41 | /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ 42 | @media only screen and (min-width: 480px) and (max-width: 767px) { 43 | } 44 | 45 | /* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ 46 | @media only screen and (max-width: 479px) { 47 | div#content div#reset-password { 48 | width: 300px; 49 | } 50 | 51 | div#content div#reset-password form#reset-password-form { 52 | padding: 20px; 53 | } 54 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | dist: trusty 3 | 4 | services: mysql 5 | 6 | before_script: 7 | # Import Database 8 | - mysql -e 'CREATE DATABASE test;' 9 | - mysql test < voj.sql 10 | 11 | before_install: 12 | # Download JDK installer 13 | - wget https://github.com/sormuras/bach/raw/master/install-jdk.sh 14 | # Fix path error on JNI headers 15 | - sudo ln -nsf $JAVA_HOME/include/linux/jni_md.h $JAVA_HOME/include/jni_md.h 16 | - sudo ln -nsf $JAVA_HOME/include/linux/jawt_md.h $JAVA_HOME/include/jawt_md.h 17 | # Setup test environment for Linux 18 | - cp judger/src/test/resources/voj-test-linux.properties judger/src/test/resources/voj-test.properties 19 | # Find Maven Installation Path for root user 20 | - sudo find / -name mvn 21 | - export MVN_EXEC=`sudo find / -name mvn` 22 | # Setup Maven for Oracle JDK 9 23 | - export MAVEN_SKIP_RC=true 24 | 25 | matrix: 26 | fast_finish: true 27 | include: 28 | - env: JDK='Oracle JDK 8' 29 | jdk: oraclejdk8 30 | 31 | - env: JDK='Open JDK 8' 32 | jdk: openjdk8 33 | 34 | - env: JDK='Oracle JDK 11' 35 | install: . ./install-jdk.sh -F 11 -L BCL 36 | 37 | - env: JDK='Open JDK 11' 38 | jdk: openjdk11 39 | 40 | allow_failures: 41 | - env: JDK='Oracle JDK 11' 42 | 43 | script: 44 | - echo PATH = ${PATH} 45 | - echo JAVA_HOME = ${JAVA_HOME} 46 | - java -version 47 | - mvn test -f web/pom.xml 48 | - sudo $MVN_EXEC test -f judger/pom.xml 49 | 50 | after_script: 51 | - mysql -e 'DROP DATABASE test;' 52 | 53 | 54 | cache: 55 | directories: 56 | - $HOME/.m2 57 | -------------------------------------------------------------------------------- /.github/workflows/sonarqube.yml: -------------------------------------------------------------------------------- 1 | name: SonarQube 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | types: [opened, synchronize, reopened] 8 | jobs: 9 | build: 10 | name: Build and analyze 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 16 | - name: Set up JDK 17 17 | uses: actions/setup-java@v4 18 | with: 19 | java-version: 17 20 | distribution: 'zulu' # Alternative distribution options are available. 21 | - name: Cache SonarQube packages 22 | uses: actions/cache@v4 23 | with: 24 | path: ~/.sonar/cache 25 | key: ${{ runner.os }}-sonar 26 | restore-keys: ${{ runner.os }}-sonar 27 | - name: Cache Maven packages 28 | uses: actions/cache@v4 29 | with: 30 | path: ~/.m2 31 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} 32 | restore-keys: ${{ runner.os }}-m2 33 | - name: Build and analyze 34 | env: 35 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_WEB }} 36 | run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=hzxie_voj_web -DskipTests -f web/pom.xml 37 | - name: Build and analyze 38 | env: 39 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_JUDGER }} 40 | run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=hzxie_voj_judger -DskipTests -f judger/pom.xml 41 | 42 | -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/administration/language-settings.css: -------------------------------------------------------------------------------- 1 | form#language-form { 2 | max-width: 750px; 3 | } 4 | 5 | form#language-form div.alert h6 { 6 | font-weight: bold; 7 | } 8 | 9 | form#language-form div.alert p:last-child { 10 | margin: 0; 11 | } 12 | 13 | form#language-form a#new-language { 14 | text-decoration: none; 15 | } 16 | 17 | form#language-form a#new-language i.fa { 18 | font-size: 16px; 19 | } 20 | 21 | form#language-form ul#languages { 22 | list-style: none; 23 | margin: 0 0 10px; 24 | } 25 | 26 | form#language-form ul#languages li.language { 27 | background-color: #fafafa; 28 | border: 2px solid #ccc; 29 | margin: 5px 0; 30 | overflow: hidden; 31 | -webkit-border-radius: 4px; 32 | -moz-border-radius: 4px; 33 | border-radius: 4px; 34 | } 35 | 36 | form#language-form ul#languages li.language div.header { 37 | height: 40px; 38 | overflow: hidden; 39 | padding: 0 10px; 40 | position: relative; 41 | } 42 | 43 | form#language-form ul#languages li.language div.header h5, 44 | form#language-form ul#languages li.language div.header ul.inline { 45 | display: inline-block; 46 | *display: inline; 47 | zoom: 1; 48 | } 49 | 50 | form#language-form ul#languages li.language div.header ul.inline { 51 | font-size: 20px; 52 | margin: 10px 0; 53 | position: absolute; 54 | right: 10px; 55 | } 56 | 57 | form#language-form ul#languages li.language div.body { 58 | border-top: 2px solid #ccc; 59 | padding: 10px 10px 0; 60 | } 61 | 62 | form#language-form ul#languages li.language div.body label { 63 | font-weight: bold; 64 | padding-top: 10px; 65 | } 66 | 67 | form#language-form ul#languages li.language div.body a { 68 | text-decoration: none; 69 | } -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/administration/edit-submission.css: -------------------------------------------------------------------------------- 1 | div.section { 2 | padding-bottom: 10px; 3 | margin-bottom: 20px; 4 | max-width: 750px; 5 | } 6 | 7 | div.section a { 8 | text-decoration: none; 9 | } 10 | 11 | div.section table td { 12 | border-top: none; 13 | } 14 | 15 | div.section table td.flag-PD, 16 | div.section table td.flag-CPL, 17 | div.section table td.flag-JUD { 18 | color: #ffa500; 19 | } 20 | 21 | div.section table td.flag-AC { 22 | color: #f00; 23 | } 24 | 25 | div.section table td.flag-PE, 26 | div.section table td.flag-WA, 27 | div.section table td.flag-TLE, 28 | div.section table td.flag-MLE, 29 | div.section table td.flag-OLE { 30 | color: #4d7aff; 31 | } 32 | 33 | div.section table td.flag-RE, 34 | div.section table td.flag-CE { 35 | color: #49a932; 36 | } 37 | 38 | div.section table td.flag-SE { 39 | color: #34495e; 40 | } 41 | 42 | div.section table tr > td:first-child { 43 | font-weight: bold; 44 | } 45 | 46 | div.section code.hljs { 47 | background-color: #f5f5f5; 48 | } 49 | 50 | /* Smaller than standard 960 (devices and browsers) */ 51 | @media only screen and (max-width: 959px) { 52 | } 53 | 54 | /* Tablet Portrait size to standard 960 (devices and browsers) */ 55 | @media only screen and (min-width: 768px) and (max-width: 959px) { 56 | } 57 | 58 | /* All Mobile Sizes (devices and browser) */ 59 | @media only screen and (max-width: 767px) { 60 | } 61 | 62 | /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ 63 | @media only screen and (min-width: 480px) and (max-width: 767px) { 64 | } 65 | 66 | /* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ 67 | @media only screen and (max-width: 479px) { 68 | } -------------------------------------------------------------------------------- /web/src/main/webapp/assets/mode/pascal.min.js: -------------------------------------------------------------------------------- 1 | /* CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | * Distributed under an MIT license: http://codemirror.net/LICENSE 3 | */!function(e){"object"==typeof exports&&"object"==typeof module?e(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],e):e(CodeMirror)}(function(e){"use strict";e.defineMode("pascal",function(){function e(e){for(var t={},r=e.split(" "),n=0;n!?|\/]/;return{startState:function(){return{tokenize:null}},token:function(e,r){if(e.eatSpace())return null;var n=(r.tokenize||t)(e,r);return"comment"==n||"meta"==n?n:n},electricChars:"{}"}}),e.defineMIME("text/x-pascal","pascal")}); -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/misc/about.css: -------------------------------------------------------------------------------- 1 | div#content { 2 | position: relative; 3 | } 4 | 5 | div#content div#ribbon { 6 | background-color: #16a085; 7 | height: 360px; 8 | position: absolute; 9 | top: 0; 10 | left: 0; 11 | right: 0; 12 | z-index: 0; 13 | } 14 | 15 | div#content div#main-content { 16 | background-color: #fff; 17 | border-radius: 4px; 18 | margin: 50px 0 10px; 19 | padding: 80px 50px; 20 | position: relative; 21 | z-index: 4; 22 | -webkit-box-shadow: 0 4px 5px 0 rgba(0,0,0,.14), 0 1px 10px 0 rgba(0,0,0,.12), 0 2px 4px -1px rgba(0,0,0,.2); 23 | -moz-box-shadow: 0 4px 5px 0 rgba(0,0,0,.14), 0 1px 10px 0 rgba(0,0,0,.12), 0 2px 4px -1px rgba(0,0,0,.2); 24 | box-shadow: 0 4px 5px 0 rgba(0,0,0,.14), 0 1px 10px 0 rgba(0,0,0,.12), 0 2px 4px -1px rgba(0,0,0,.2); 25 | } 26 | 27 | div#content div#main-content div.section { 28 | margin-bottom: 40px; 29 | } 30 | 31 | div#content div#main-content div.section p { 32 | line-height: 150%; 33 | } 34 | 35 | div#content div#main-content h3 { 36 | margin-bottom: 20px; 37 | } 38 | 39 | div#content div#main-content ul.languages { 40 | list-style: none; 41 | margin: 0; 42 | } 43 | 44 | div#sidebar-nav { 45 | color: #fff; 46 | margin-top: 60px; 47 | position: relative; 48 | } 49 | 50 | div#sidebar-nav h5 { 51 | margin-bottom: 15px; 52 | text-transform: none; 53 | } 54 | 55 | div#sidebar-nav ul.contents { 56 | list-style: none; 57 | margin: 0; 58 | } 59 | 60 | div#sidebar-nav ul.contents a { 61 | color: #fff; 62 | display: block; 63 | padding: 6px 0 6px 14px; 64 | } 65 | 66 | div#sidebar-nav ul.contents a:hover { 67 | background-color: #1abc9c; 68 | } 69 | 70 | @media only screen and (max-width: 767px) { 71 | div#sidebar-nav { 72 | display: none; 73 | } 74 | } -------------------------------------------------------------------------------- /web/src/main/resources/mappers/CheckpointMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 23 | 31 | 34 | INSERT INTO voj_problem_checkpoints (problem_id, checkpoint_id, checkpoint_exactly_match, checkpoint_score, checkpoint_input, checkpoint_output) 35 | VALUES (#{problemId}, #{checkpointId}, #{isExactlyMatch}, #{score}, #{input}, #{output}) 36 | 37 | 40 | DELETE FROM voj_problem_checkpoints 41 | WHERE problem_id = #{problemId} 42 | 43 | -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/administration/all-users.css: -------------------------------------------------------------------------------- 1 | div#content a { 2 | text-decoration: none; 3 | } 4 | 5 | div#filters form { 6 | margin: 0; 7 | } 8 | 9 | div#content table { 10 | border: 1px solid #e8e8e8; 11 | margin: 20px 0 0; 12 | -webkit-box-shadow: 0 0 5px rgba(0,0,0,0.1); 13 | -moz-box-shadow: 0 0 5px rgba(0,0,0,0.1); 14 | box-shadow: 0 0 5px rgba(0,0,0,0.1); 15 | } 16 | 17 | div#content table thead { 18 | background-color: #16a085; 19 | color: #fff; 20 | } 21 | 22 | div#content table thead label.checkbox.checked span.icons { 23 | color: #fff; 24 | } 25 | 26 | div#content table label.checkbox { 27 | margin: 0; 28 | padding: 0; 29 | } 30 | 31 | div#content table th { 32 | border-left: 1px solid #e8e8e8; 33 | text-align: center; 34 | vertical-align: middle; 35 | } 36 | 37 | div#content table th.check-box, 38 | div#content table td.check-box { 39 | width: 20px; 40 | } 41 | 42 | div#content table td { 43 | border-left: 1px solid #e8e8e8; 44 | color: #666; 45 | text-align: center; 46 | vertical-align: middle; 47 | } 48 | 49 | /* Smaller than standard 960 (devices and browsers) */ 50 | @media only screen and (max-width: 959px) { 51 | } 52 | 53 | /* Tablet Portrait size to standard 960 (devices and browsers) */ 54 | @media only screen and (min-width: 768px) and (max-width: 959px) { 55 | } 56 | 57 | /* All Mobile Sizes (devices and browser) */ 58 | @media only screen and (max-width: 767px) { 59 | div#filters button.btn-danger { 60 | margin-bottom: 10px; 61 | } 62 | 63 | div#filters button.btn { 64 | width: 100%; 65 | } 66 | } 67 | 68 | /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ 69 | @media only screen and (min-width: 480px) and (max-width: 767px) { 70 | } 71 | 72 | /* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ 73 | @media only screen and (max-width: 479px) { 74 | } -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/administration/all-problems.css: -------------------------------------------------------------------------------- 1 | div#content a { 2 | text-decoration: none; 3 | } 4 | 5 | div#filters form { 6 | margin: 0; 7 | } 8 | 9 | div#content table { 10 | border: 1px solid #e8e8e8; 11 | margin: 20px 0 0; 12 | -webkit-box-shadow: 0 0 5px rgba(0,0,0,0.1); 13 | -moz-box-shadow: 0 0 5px rgba(0,0,0,0.1); 14 | box-shadow: 0 0 5px rgba(0,0,0,0.1); 15 | } 16 | 17 | div#content table thead { 18 | background-color: #16a085; 19 | color: #fff; 20 | } 21 | 22 | div#content table thead label.checkbox.checked span.icons { 23 | color: #fff; 24 | } 25 | 26 | div#content table label.checkbox { 27 | margin: 0; 28 | padding: 0; 29 | } 30 | 31 | div#content table th { 32 | border-left: 1px solid #e8e8e8; 33 | text-align: center; 34 | vertical-align: middle; 35 | } 36 | 37 | div#content table th.check-box, 38 | div#content table td.check-box { 39 | width: 20px; 40 | } 41 | 42 | div#content table td { 43 | border-left: 1px solid #e8e8e8; 44 | color: #666; 45 | text-align: center; 46 | vertical-align: middle; 47 | } 48 | 49 | /* Smaller than standard 960 (devices and browsers) */ 50 | @media only screen and (max-width: 959px) { 51 | } 52 | 53 | /* Tablet Portrait size to standard 960 (devices and browsers) */ 54 | @media only screen and (min-width: 768px) and (max-width: 959px) { 55 | } 56 | 57 | /* All Mobile Sizes (devices and browser) */ 58 | @media only screen and (max-width: 767px) { 59 | div#filters button.btn-danger { 60 | margin-bottom: 10px; 61 | } 62 | 63 | div#filters button.btn { 64 | width: 100%; 65 | } 66 | } 67 | 68 | /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ 69 | @media only screen and (min-width: 480px) and (max-width: 767px) { 70 | } 71 | 72 | /* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ 73 | @media only screen and (max-width: 479px) { 74 | } -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/accounts/register.css: -------------------------------------------------------------------------------- 1 | div#header { 2 | border-top: 4px solid #1abc9c; 3 | } 4 | 5 | div#content div#register { 6 | margin: 20px auto 20px; 7 | width: 400px; 8 | } 9 | 10 | div#content div#register h2 { 11 | color: #1abc9c; 12 | } 13 | 14 | div#content div#register div.alert { 15 | margin-top: 20px; 16 | font-size: 13px; 17 | } 18 | 19 | div#content div#register form#register-form { 20 | background: #fff; 21 | border: 1px solid #dedede; 22 | color: #666; 23 | font-size: 12px; 24 | margin: 20px auto; 25 | padding: 40px; 26 | -webkit-border-radius: 4px; 27 | -moz-border-radius: 4px; 28 | border-radius: 4px; 29 | } 30 | 31 | div#content div#register form#register-form label { 32 | font-size: 12px; 33 | } 34 | 35 | div#content div#register form#register-form select#language-preference { 36 | border: 2px solid #bdc3c7; 37 | color: #34495e; 38 | height: 41px; 39 | -webkit-border-radius: 6px; 40 | -moz-border-radius: 6px; 41 | border-radius: 6px; 42 | -webkit-transition: border .25s linear,color .25s linear; 43 | -moz-transition: border .25s linear,color .25s linear; 44 | transition: border .25s linear,color .25s linear; 45 | } 46 | 47 | div#content div#register form#register-form select#language-preference:focus { 48 | border-color: #1abc9c 49 | } 50 | 51 | /* Smaller than standard 960 (devices and browsers) */ 52 | /* Tablet Portrait size to standard 960 (devices and browsers) */ 53 | @media only screen and (min-width: 768px) and (max-width: 959px) { 54 | } 55 | 56 | /* All Mobile Sizes (devices and browser) */ 57 | /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ 58 | @media only screen and (min-width: 480px) and (max-width: 767px) { 59 | } 60 | 61 | /* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ 62 | @media only screen and (max-width: 479px) { 63 | div#content div#register { 64 | width: 300px; 65 | } 66 | 67 | div#content div#register form#register-form { 68 | padding: 20px; 69 | } 70 | } -------------------------------------------------------------------------------- /docker/judger/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for Verwandlung Online Judge - Judger 2 | FROM ubuntu:24.04 3 | MAINTAINER Haozhe Xie "root@haozhexie.com" 4 | 5 | # User Settings 6 | ARG MYSQL_USER_PASS=U3bEwhRHnD6xNVpb 7 | ARG MYSQL_HOST=voj.web 8 | ARG MYSQL_PORT=3306 9 | ARG ACTIVEMQ_HOST=voj.web 10 | ARG ACTIVEMQ_PORT=61616 11 | 12 | # Set environment variables. 13 | ENV HOME /root 14 | ENV JAVA_HOME /usr/lib/jvm/java-21-openjdk-amd64 15 | ENV M2_HOME /opt/maven 16 | 17 | # Define working directory. 18 | WORKDIR /root 19 | 20 | # Install Java and Python 21 | RUN apt-get update && \ 22 | apt-get install -y git curl wget make g++ openjdk-21-jdk python3 && \ 23 | # Setup Python3 as the default Python 24 | ln -s /usr/bin/python3 /usr/bin/python && \ 25 | # Install Maven 26 | MAVEN_VERSION=$(curl -s https://maven.apache.org/download.cgi | grep -oP 'apache-maven-\K[0-9]+\.[0-9]+\.[0-9]+' | head -1) && \ 27 | wget https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz && \ 28 | tar -xf apache-maven-${MAVEN_VERSION}-bin.tar.gz -C /opt && \ 29 | mv /opt/apache-maven-${MAVEN_VERSION} /opt/maven && \ 30 | rm apache-maven-${MAVEN_VERSION}-bin.tar.gz 31 | 32 | # Setup Judger Project 33 | RUN git clone https://github.com/hzxie/voj.git && \ 34 | sed -i "s@jdbc.url = jdbc:mysql://localhost:3306@jdbc.url = jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}@g" voj/judger/src/main/resources/voj.properties && \ 35 | sed -i "s/jdbc.username = root/jdbc.username = voj/g" voj/judger/src/main/resources/voj.properties && \ 36 | sed -i "s/jdbc.password = /jdbc.password = ${MYSQL_USER_PASS}/g" voj/judger/src/main/resources/voj.properties && \ 37 | sed -i "s/localhost:61616/${ACTIVEMQ_HOST}:${ACTIVEMQ_PORT}/g" voj/judger/src/main/resources/voj.properties && \ 38 | mkdir -p voj/target/classes && \ 39 | $M2_HOME/bin/mvn package -DskipTests -f voj/judger/pom.xml 40 | 41 | # Run Judger 42 | CMD ["java", "-jar", "voj/judger/target/voj.judger.jar"] 43 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | executors: 4 | java-executor: 5 | docker: 6 | - image: cimg/openjdk:24.0.2 7 | working_directory: ~/project 8 | 9 | jobs: 10 | test: 11 | parameters: 12 | java_version: 13 | type: string 14 | default: "24.0.2" 15 | docker: 16 | - image: cimg/openjdk:<< parameters.java_version >> 17 | working_directory: ~/project 18 | steps: 19 | - checkout 20 | 21 | - run: 22 | name: Install MySQL Server 23 | command: | 24 | sudo apt-get update 25 | sudo apt-get install -y mysql-server 26 | sudo service mysql start 27 | sudo mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '';" 28 | sudo mysql -e "FLUSH PRIVILEGES;" 29 | 30 | - run: 31 | name: Fix JNI headers on Linux 32 | command: | 33 | sudo cp $JAVA_HOME/include/linux/jni_md.h $JAVA_HOME/include/jni_md.h 34 | sudo cp $JAVA_HOME/include/linux/jawt_md.h $JAVA_HOME/include/jawt_md.h 35 | 36 | - run: 37 | name: Initialize test database 38 | command: | 39 | sudo mysql -e "CREATE DATABASE test;" 40 | sudo mysql -e "SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));" 41 | sudo mysql test < voj.sql 42 | 43 | - run: 44 | name: Setup test environment for Linux 45 | command: cp judger/src/test/resources/voj-test-linux.properties judger/src/test/resources/voj-test.properties 46 | 47 | - run: 48 | name: Run Maven tests (web) 49 | command: mvn test -f web/pom.xml 50 | 51 | - run: 52 | name: Run Maven tests (judger) 53 | command: mvn test -f judger/pom.xml 54 | 55 | - run: 56 | name: Drop test database 57 | command: sudo mysql -e "DROP DATABASE test;" 58 | 59 | workflows: 60 | test-matrix: 61 | jobs: 62 | - test: 63 | name: test-java17 64 | java_version: "17.0" 65 | - test: 66 | name: test-java21 67 | java_version: "21.0" 68 | - test: 69 | name: test-java24 70 | java_version: "24.0" 71 | 72 | -------------------------------------------------------------------------------- /web/src/main/java/org/verwandlung/voj/web/exception/ResourceNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.web.exception; 41 | 42 | import org.springframework.http.HttpStatus; 43 | import org.springframework.web.bind.annotation.ResponseStatus; 44 | 45 | @ResponseStatus(HttpStatus.NOT_FOUND) 46 | public class ResourceNotFoundException extends RuntimeException { 47 | /** 唯一的序列化标识符. */ 48 | private static final long serialVersionUID = -7683678924179048862L; 49 | } 50 | -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/administration/edit-user.css: -------------------------------------------------------------------------------- 1 | form#profile-form { 2 | max-width: 750px; 3 | } 4 | 5 | form#profile-form label { 6 | font-weight: bold; 7 | } 8 | 9 | form#profile-form label > a { 10 | font-weight: normal; 11 | } 12 | 13 | form#profile-form input[type=text], 14 | form#profile-form textarea { 15 | resize: none; 16 | width: 100%; 17 | } 18 | 19 | form#profile-form a#new-social-link { 20 | float: right; 21 | font-size: 20px; 22 | } 23 | 24 | form#profile-form div#social-links ul { 25 | list-style: none; 26 | margin: 0 0 10px; 27 | } 28 | 29 | form#profile-form div#social-links li.social-link { 30 | background-color: #fafafa; 31 | border: 2px solid #ccc; 32 | margin: 5px 0; 33 | overflow: hidden; 34 | -webkit-border-radius: 4px; 35 | -moz-border-radius: 4px; 36 | border-radius: 4px; 37 | } 38 | 39 | form#profile-form div#social-links li.social-link div.header { 40 | height: 40px; 41 | overflow: hidden; 42 | padding: 0 10px; 43 | position: relative; 44 | } 45 | 46 | form#profile-form div#social-links li.social-link div.header h5, 47 | form#profile-form div#social-links li.social-link div.header ul.inline { 48 | display: inline-block; 49 | *display: inline; 50 | zoom: 1; 51 | } 52 | 53 | form#profile-form div#social-links li.social-link div.header ul.inline { 54 | font-size: 20px; 55 | margin: 10px 0; 56 | position: absolute; 57 | right: 10px; 58 | } 59 | 60 | form#profile-form div#social-links li.social-link div.body { 61 | border-top: 2px solid #ccc; 62 | padding: 10px 10px 0; 63 | } 64 | 65 | form#profile-form div#markdown-editor { 66 | margin-bottom: 20px; 67 | } 68 | 69 | /* Smaller than standard 960 (devices and browsers) */ 70 | @media only screen and (max-width: 959px) { 71 | } 72 | 73 | /* Tablet Portrait size to standard 960 (devices and browsers) */ 74 | @media only screen and (min-width: 768px) and (max-width: 959px) { 75 | } 76 | 77 | /* All Mobile Sizes (devices and browser) */ 78 | @media only screen and (max-width: 767px) { 79 | } 80 | 81 | /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ 82 | @media only screen and (min-width: 480px) and (max-width: 767px) { 83 | } 84 | 85 | /* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ 86 | @media only screen and (max-width: 479px) { 87 | } -------------------------------------------------------------------------------- /web/src/main/resources/mappers/UserMetaMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 27 | 35 | 43 | 49 | INSERT INTO voj_usermeta (uid, meta_key, meta_value) 50 | VALUES (#{user.uid}, #{metaKey}, #{metaValue}) 51 | 52 | 55 | UPDATE voj_usermeta 56 | SET meta_value = #{metaValue} 57 | WHERE meta_id = #{metaId} 58 | 59 | 62 | DELETE FROM voj_usermeta 63 | WHERE uid = #{uid} 64 | 65 | -------------------------------------------------------------------------------- /web/src/main/resources/mappers/LanguageMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 24 | 32 | 38 | 44 | INSERT INTO voj_languages (language_id, language_slug, language_name, language_compile_command, language_run_command) 45 | VALUES (#{languageId}, #{languageSlug}, #{languageName}, #{compileCommand}, #{runCommand}) 46 | 47 | 50 | UPDATE voj_languages 51 | SET language_slug = #{languageSlug}, language_name = #{languageName}, language_compile_command = #{compileCommand}, language_run_command = #{runCommand} 52 | WHERE language_id = #{languageId} 53 | 54 | 57 | DELETE FROM voj_languages 58 | WHERE language_id = #{languageId} 59 | 60 | -------------------------------------------------------------------------------- /web/src/main/webapp/assets/js/site.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Global JavaScript by @zjhzxhz 3 | * 4 | * Copyright 2015 Contributors 5 | * Released under the GPL v3 license 6 | * http://opensource.org/licenses/GPL-3.0 7 | */ 8 | /* String Protorype Extension */ 9 | String.prototype.format = function() { 10 | var newStr = this, i = 0; 11 | while (/%s/.test(newStr)) { 12 | newStr = newStr.replace("%s", arguments[i++]) 13 | } 14 | return newStr; 15 | } 16 | 17 | /* Format DateTime for different locales */ 18 | function getFormatedDateString(dateTime, locale) { 19 | var dateObject = new Date(dateTime), 20 | dateString = dateObject.toString(); 21 | 22 | if ( locale == 'en_US' ) { 23 | dateString = dateObject.toString('MMM d, yyyy h:mm:ss tt'); 24 | } else if ( locale == 'zh_CN' ) { 25 | dateString = dateObject.toString('yyyy年M月d日 HH:mm:ss'); 26 | } 27 | return dateString; 28 | } 29 | 30 | /* JavaScript for DrawerMenu */ 31 | function openDrawerMenu() { 32 | $('#drawer-nav').animate({ 33 | right: 0 34 | }, 100); 35 | } 36 | 37 | function closeDrawerMenu() { 38 | $('#drawer-nav').animate({ 39 | right: -320 40 | }, 100); 41 | } 42 | 43 | /* Display the avatar of the user in DrawerMenu */ 44 | $(function() { 45 | var imageObject = $('img', '#drawer-nav #profile'), 46 | email = $('p.email', '#drawer-nav #profile').html(); 47 | 48 | if ( typeof(email) == 'undefined' ) { 49 | return; 50 | } 51 | $.ajax({ 52 | type: 'GET', 53 | url: 'https://secure.gravatar.com/' + md5(email) + '.json', 54 | dataType: 'jsonp', 55 | success: function(result){ 56 | if ( result != null ) { 57 | var imageUrl = result['entry'][0]['thumbnailUrl'], 58 | requrestUrl = imageUrl + '?s=200'; 59 | $(imageObject).attr('src', requrestUrl); 60 | } 61 | } 62 | }); 63 | }); 64 | 65 | /* Display Current Language Name */ 66 | $(function() { 67 | var languageMapper = { 68 | 'en_US': 'English', 69 | 'zh_CN': '简体中文' 70 | }; 71 | var currentLanguageCode = $('#current-language').html(), 72 | currentLanguageName = languageMapper[currentLanguageCode]; 73 | 74 | $('#current-language').html(currentLanguageName); 75 | }); -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # # version: "{branch} {build}" 2 | image: 3 | # Windows Server 2019 4 | - Visual Studio 2019 5 | 6 | environment: 7 | appveyor_build_worker_cloud: gce 8 | appveyor_rdp_password: Fx8eHPNesxhpmdru 9 | matrix: 10 | - JAVA_HOME: C:\Program Files\Java\jdk17 11 | - JAVA_HOME: C:\Program Files\Java\jdk21 12 | 13 | init: 14 | - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) 15 | - ps: Start-Service MySQL80 16 | 17 | install: 18 | - set MINGW_HOME=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64 19 | - set PATH=%MINGW_HOME%\bin;C:\cygwin64\bin;C:\Program Files\MySql\MySQL Server 8.0\bin;%PATH% 20 | # Fix bug for mkdir in AppVeyor 21 | - del "C:\Program Files\Git\usr\bin\mkdir.exe" 22 | # Fix path error for make 23 | - copy "C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin\mingw32-make.exe" "C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin\make.exe" 24 | # Fix path error on JNI headers 25 | - copy "C:\Program Files\Java\jdk17\include\win32\jni_md.h" "C:\Program Files\Java\jdk17\include\jni_md.h" 26 | - copy "C:\Program Files\Java\jdk17\include\win32\jawt_md.h" "C:\Program Files\Java\jdk17\include\jawt_md.h" 27 | - copy "C:\Program Files\Java\jdk21\include\win32\jni_md.h" "C:\Program Files\Java\jdk21\include\jni_md.h" 28 | - copy "C:\Program Files\Java\jdk21\include\win32\jawt_md.h" "C:\Program Files\Java\jdk21\include\jawt_md.h" 29 | # Setup test environment for Windows 30 | - copy "judger\src\test\resources\voj-test-windows.properties" "judger\src\test\resources\voj-test.properties" 31 | 32 | build: off 33 | 34 | before_test: 35 | - mysql -e "SET PASSWORD FOR 'root'@'localhost' = '';" --user=root --password=Password12! 36 | - mysql -e "CREATE DATABASE test;" --user=root 37 | - mysql -e "SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));" --user=root 38 | - mysql test < voj.sql --user=root 39 | 40 | test_script: 41 | - mvn test -f web\pom.xml 42 | - mvn test -f judger\pom.xml 43 | 44 | after_test: 45 | - mysql -e "DROP DATABASE test;" --user=root 46 | 47 | on_finish: 48 | - ps: $blockRdp = $false; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) 49 | 50 | matrix: 51 | fast_finish: false 52 | 53 | cache: 54 | - C:\maven\ 55 | - C:\Users\appveyor\.m2 56 | -------------------------------------------------------------------------------- /judger/src/main/java/org/verwandlung/voj/judger/exception/CreateDirectoryException.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.judger.exception; 41 | 42 | /** 43 | * 创建文件夹失败的IO异常. 当java.io.File.File.mkdirs()返回false时被抛出. 44 | * 45 | * @author Haozhe Xie 46 | */ 47 | public class CreateDirectoryException extends Exception { 48 | /** 49 | * IOException的构造函数. 50 | * 51 | * @param message - 错误消息 52 | */ 53 | public CreateDirectoryException(String message) { 54 | super(message); 55 | } 56 | 57 | /** 唯一的序列化标识符. */ 58 | private static final long serialVersionUID = 7430055519184434330L; 59 | } 60 | -------------------------------------------------------------------------------- /judger/src/main/java/org/verwandlung/voj/judger/exception/IllgealSubmissionException.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.judger.exception; 41 | 42 | /** 43 | * 无效的提交记录异常. 当getSubmission(long)操作返回null时被抛出. 44 | * 45 | * @author Haozhe Xie 46 | */ 47 | public class IllgealSubmissionException extends Exception { 48 | /** 49 | * IllgealSubmissionException的构造函数. 50 | * 51 | * @param message - 错误消息 52 | */ 53 | public IllgealSubmissionException(String message) { 54 | super(message); 55 | } 56 | 57 | /** 唯一的序列化标识符. */ 58 | private static final long serialVersionUID = -9019235951964656553L; 59 | } 60 | -------------------------------------------------------------------------------- /web/src/main/resources/mappers/DiscussionTopicMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 28 | 35 | 41 | INSERT INTO voj_discussion_topics(discussion_topic_slug, discussion_topic_name, discussion_parent_topic_id) 42 | VALUES (#{discussionTopicSlug}, #{discussionTopicName}, #{parentDiscussionTopicId}) 43 | 44 | 47 | UPDATE voj_discussion_topics 48 | SET discussion_topic_slug = #{discussionTopicSlug}, discussion_topic_name = #{discussionTopicName}, discussion_parent_topic_id = #{parentDiscussionTopicId} 49 | WHERE discussion_topic_id = #{discussionTopicId} 50 | 51 | 54 | DELETE FROM voj_discussion_topics 55 | WHERE discussion_topic_id = #{discussionTopicId} 56 | 57 | 58 | -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/highlight.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Syntax highlighting with language autodetection. 3 | * https://highlightjs.org/ 4 | */ 5 | .hljs{display:block;overflow-x:auto;padding:.5em;background:#f0f0f0;-webkit-text-size-adjust:none}.hljs,.hljs-subst,.hljs-tag .hljs-title,.nginx .hljs-title{color:#000}.apache .hljs-cbracket,.apache .hljs-tag,.asciidoc .hljs-header,.bash .hljs-variable,.coffeescript .hljs-attribute,.django .hljs-variable,.erlang_repl .hljs-function_or_atom,.haml .hljs-symbol,.hljs-addition,.hljs-constant,.hljs-flow,.hljs-name,.hljs-parent,.hljs-pragma,.hljs-preprocessor,.hljs-rule .hljs-value,.hljs-stream,.hljs-string,.hljs-tag .hljs-value,.hljs-template_tag,.hljs-title,.markdown .hljs-header,.pf .hljs-variable,.ruby .hljs-symbol,.ruby .hljs-symbol .hljs-string,.smalltalk .hljs-class,.tex .hljs-command,.tex .hljs-special,.tp .hljs-variable{color:#800}.asciidoc .hljs-blockquote,.diff .hljs-header,.hljs-annotation,.hljs-chunk,.hljs-comment,.markdown .hljs-blockquote,.smartquote{color:#888}.asciidoc .hljs-bullet,.asciidoc .hljs-link_url,.go .hljs-constant,.hljs-change,.hljs-date,.hljs-hexcolor,.hljs-literal,.hljs-number,.hljs-regexp,.lasso .hljs-variable,.makefile .hljs-variable,.markdown .hljs-bullet,.markdown .hljs-link_url,.smalltalk .hljs-char,.smalltalk .hljs-symbol{color:#0086b3}.apache .hljs-sqbracket,.asciidoc .hljs-attribute,.asciidoc .hljs-link_label,.clojure .hljs-attribute,.coffeescript .hljs-property,.erlang_repl .hljs-reserved,.haml .hljs-bullet,.hljs-array,.hljs-attr_selector,.hljs-decorator,.hljs-deletion,.hljs-doctype,.hljs-envvar,.hljs-filter .hljs-argument,.hljs-important,.hljs-label,.hljs-localvars,.hljs-phony,.hljs-pi,.hljs-prompt,.hljs-pseudo,.hljs-shebang,.lasso .hljs-attribute,.markdown .hljs-link_label,.nginx .hljs-built_in,.ruby .hljs-string,.tex .hljs-formula,.vhdl .hljs-attribute{color:#88f}.apache .hljs-tag,.asciidoc .hljs-strong,.bash .hljs-variable,.css .hljs-tag,.hljs-built_in,.hljs-doctag,.hljs-id,.hljs-keyword,.hljs-request,.hljs-status,.hljs-title,.hljs-type,.hljs-typename,.hljs-winutils,.markdown .hljs-strong,.pf .hljs-variable,.smalltalk .hljs-class,.tex .hljs-command,.tp .hljs-data,.tp .hljs-io{font-weight:700}.asciidoc .hljs-emphasis,.markdown .hljs-emphasis,.tp .hljs-units{font-style:italic}.nginx .hljs-built_in{font-weight:400}.coffeescript .javascript,.javascript .xml,.lasso .markup,.tex .hljs-formula,.xml .css,.xml .hljs-cdata,.xml .javascript,.xml .vbscript{opacity:.5}.hljs-keyword{color:#a71d5d}.hljs-preprocessor{color:#183691}.hljs-title{color:#795da3}.hljs-number{color:#0086b3} -------------------------------------------------------------------------------- /web/src/test/java/org/verwandlung/voj/web/util/MessageReceiverTest.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.web.util; 41 | 42 | import org.springframework.beans.factory.annotation.Autowired; 43 | 44 | import org.verwandlung.voj.web.messenger.MessageReceiver; 45 | 46 | /** 47 | * MessageReceiver的测试类. 注意: 仅供开发调试使用. 48 | * 49 | * @author Haozhe Xie 50 | */ 51 | /* 52 | @ExtendWith(SpringExtension.class) 53 | @ContextConfiguration({"classpath:test-spring-context.xml"}) 54 | */ 55 | public class MessageReceiverTest { 56 | /* @Test */ 57 | public void testOnMessage() throws InterruptedException { 58 | Thread.sleep(5000); 59 | } 60 | 61 | /** 自动注入的MessageReceiver对象. */ 62 | @Autowired private MessageReceiver messageReceiver; 63 | } 64 | -------------------------------------------------------------------------------- /web/src/main/java/org/verwandlung/voj/web/util/HttpRequestParser.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.web.util; 41 | 42 | import jakarta.servlet.http.HttpServletRequest; 43 | 44 | /** 45 | * Http请求头解析服务. 46 | * 47 | * @author Haozhe Xie 48 | */ 49 | public class HttpRequestParser { 50 | /** Utility classes should not have a public constructor. */ 51 | private HttpRequestParser() {} 52 | 53 | /** 54 | * 在使用反向代理情况下解析用户的真实IP. 55 | * 56 | * @param request - HttpRequest对象 57 | * @return 用户的真实IP 58 | */ 59 | public static String getRemoteAddr(HttpServletRequest request) { 60 | if (request.getHeader("X-Real-IP") != null) { 61 | return request.getHeader("X-Real-IP"); 62 | } 63 | return request.getRemoteAddr(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /judger/src/main/java/org/verwandlung/voj/judger/messenger/MessageSender.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.judger.messenger; 41 | 42 | import java.util.Map; 43 | 44 | import org.springframework.beans.factory.annotation.Autowired; 45 | import org.springframework.jms.core.JmsTemplate; 46 | import org.springframework.stereotype.Component; 47 | 48 | /** 49 | * 消息发送服务. 50 | * 51 | * @author Haozhe Xie 52 | */ 53 | @Component 54 | public class MessageSender { 55 | /** 56 | * 发送消息至消息队列. 57 | * 58 | * @param mapMessage - Key-Value格式的消息 59 | */ 60 | public void sendMessage(final Map mapMessage) { 61 | jmsTemplate.convertAndSend(mapMessage); 62 | } 63 | 64 | /** 自动注入的JmsTemplate对象. 用于发送消息至消息队列. */ 65 | @Autowired private JmsTemplate jmsTemplate; 66 | } 67 | -------------------------------------------------------------------------------- /web/src/main/resources/mappers/BulletinBoardMessageMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 30 | 38 | 41 | INSERT INTO voj_bulletin_board_messages(message_title, message_body, message_create_time) 42 | VALUES (messageTitle, messageBody, messageCreateTime) 43 | 44 | 47 | UPDATE voj_bulletin_board_messages 48 | SET message_title = #{messageTitle}, message_body = #{messageBody}, message_create_time = #{messageCreateTime} 49 | WHERE message_id = #{messageId} 50 | 51 | 54 | DELETE FROM voj_bulletin_board_messages 55 | WHERE message_id = #{messageId} 56 | 57 | -------------------------------------------------------------------------------- /web/src/test/java/org/verwandlung/voj/web/util/SlugifyUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.web.util; 41 | 42 | import org.junit.jupiter.api.Assertions; 43 | import org.junit.jupiter.api.Test; 44 | 45 | /** 46 | * SlugifyUtils测试类. 47 | * 48 | * @author Haozhe Xie 49 | */ 50 | public class SlugifyUtilsTest { 51 | /** 测试用例: 测试getSlug(String)方法 测试数据: Latin字符组成的字符串 预期结果: 返回将Latin字符转为小写并除去其中的空格的字符串 */ 52 | @Test 53 | public void testGetSlugWithAsciiChars() { 54 | Assertions.assertEquals("a-b--c", SlugifyUtils.getSlug("a b -C")); 55 | } 56 | 57 | /** 测试用例: 测试getSlug(String)方法 测试数据: Latin字符和非Latin字符组成的字符串 预期结果: 返回将非Latin字符转义后并除去其中的空格的字符串 */ 58 | @Test 59 | public void testGetSlugWithChineseCharacters() { 60 | Assertions.assertEquals("a%e4%b8%ad%e6%96%87-b", SlugifyUtils.getSlug("a中文 b")); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /web/src/main/resources/mappers/ContestSubmissionMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 12 | 16 | 17 | 18 | 19 | 28 | 38 | 47 | 58 | 61 | INSERT INTO voj_contest_submissions (contest_id, submission_id) 62 | VALUES (#{contest.contestId}, #{submission.submissionId}) 63 | 64 | 67 | DELETE FROM voj_contest_submissions 68 | WHERE contest_id = #{contestId} 69 | AND submission_id = #{submissionId} 70 | 71 | -------------------------------------------------------------------------------- /web/src/main/resources/mappers/ContestMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 29 | 43 | 51 | 54 | INSERT INTO voj_contests (contest_name, contest_notes, contest_start_time, contest_end_time, contest_mode, contest_problems) 55 | VALUES (#{contestName}, #{contestNotes}, #{startTime}, #{endTime}, #{contestMode}, #{problems}) 56 | 57 | 60 | UPDATE voj_contests 61 | SET contest_name = #{contestName}, contest_notes = #{contestNotes}, contest_start_time = #{startTime}, contest_end_time = #{endTime}, contest_mode = #{contestMode}, contest_problems = #{problems} 62 | WHERE contest_id = #{contestId} 63 | 64 | 67 | DELETE FROM voj_contests 68 | WHERE contest_id = #{contestId} 69 | 70 | -------------------------------------------------------------------------------- /web/src/main/java/org/verwandlung/voj/web/util/DateUtils.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.web.util; 41 | 42 | import java.util.Calendar; 43 | import java.util.Date; 44 | import java.util.GregorianCalendar; 45 | 46 | /** 47 | * Calendar和Date的辅助类. 48 | * 49 | * @author Haozhe Xie 50 | */ 51 | public class DateUtils { 52 | /** 53 | * 获取几天前的某一天的Date对象. 54 | * 55 | * @param period - 与当前时间的间隔(以天为单位) 56 | * @return 几天前的某一天的Date对象 57 | */ 58 | public static Date getPreviousDate(int period) { 59 | Date today = new Date(); 60 | Calendar calendar = new GregorianCalendar(); 61 | calendar.setTime(today); 62 | 63 | if (period == 7) { 64 | calendar.add(Calendar.DATE, -7); 65 | } else if (period == 30) { 66 | calendar.add(Calendar.MONTH, -1); 67 | } else { 68 | calendar.add(Calendar.YEAR, -1); 69 | } 70 | return calendar.getTime(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /web/src/test/java/org/verwandlung/voj/web/util/HtmlTextFilterTest.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.web.util; 41 | 42 | import org.junit.jupiter.api.Assertions; 43 | import org.junit.jupiter.api.Test; 44 | 45 | /** 46 | * TextFilter的测试类. 47 | * 48 | * @author Haozhe Xie 49 | */ 50 | public class HtmlTextFilterTest { 51 | /** 测试用例: 测试filterHtml()方法. 测试数据: 匹配的HTML的字符串. 与其结果: 返回过滤HTML的字符串 */ 52 | @Test 53 | public void testFilterHtmlUsingMatchedHtml() { 54 | String text = "XSS ."; 55 | Assertions.assertEquals("XSS .", HtmlTextFilter.filter(text)); 56 | } 57 | 58 | /** 测试用例: 测试filterHtml()方法. 测试数据: 不匹配的HTML的字符串. 与其结果: 返回过滤HTML的字符串 */ 59 | @Test 60 | public void testFilterHtmlUsingUnmatchedHtml() { 61 | String text = "XSS alert('XSS')."; 62 | Assertions.assertEquals("XSS alert('XSS').", HtmlTextFilter.filter(text)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /web/src/test/java/org/verwandlung/voj/web/util/MessageSenderTest.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.web.util; 41 | 42 | import java.util.HashMap; 43 | import java.util.Map; 44 | 45 | import org.springframework.beans.factory.annotation.Autowired; 46 | 47 | import org.verwandlung.voj.web.messenger.MessageSender; 48 | 49 | /** 50 | * MessageSender的测试类. 注意: 仅供开发调试使用. 51 | * 52 | * @author Haozhe Xie 53 | */ 54 | /* 55 | @ExtendWith(SpringExtension.class) 56 | @ContextConfiguration({"classpath:test-spring-context.xml"}) 57 | */ 58 | public class MessageSenderTest { 59 | /** 测试用例: 测试sendMessage()方法 测试数据: N/a 预期结果: ActiveMQ的消息队列中产生预期的消息 */ 60 | /* @Test */ 61 | public void testSendMessage() { 62 | Map mapMessage = new HashMap<>(); 63 | mapMessage.put("message", "Hello World"); 64 | messageSender.sendMessage(mapMessage); 65 | } 66 | 67 | /** 自动注入的MessageSender对象. */ 68 | @Autowired private MessageSender messageSender; 69 | } 70 | -------------------------------------------------------------------------------- /web/src/main/java/org/verwandlung/voj/web/mapper/JudgeResultMapper.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.web.mapper; 41 | 42 | import org.apache.ibatis.annotations.CacheNamespace; 43 | import org.apache.ibatis.annotations.Param; 44 | 45 | import org.verwandlung.voj.web.model.JudgeResult; 46 | 47 | /** 48 | * JudgeResult Data Access Object. 49 | * 50 | * @author Haozhe Xie 51 | */ 52 | @CacheNamespace(implementation = org.mybatis.caches.ehcache.EhcacheCache.class) 53 | public interface JudgeResultMapper { 54 | /** 55 | * 通过评测结果的唯一标识符获取评测结果对象. 56 | * 57 | * @param judgeResultId - 评测结果的唯一标识符 58 | * @return 预期的评测结果对象或空引用 59 | */ 60 | JudgeResult getJudgeResultUsingId(@Param("judgeResultId") int judgeResultId); 61 | 62 | /** 63 | * 通过评测结果的别名获取评测结果对象. 64 | * 65 | * @param judgeResultSlug - 评测结果的别名 66 | * @return 预期的评测结果对象或空引用 67 | */ 68 | JudgeResult getJudgeResultUsingSlug(@Param("judgeResultSlug") String judgeResultSlug); 69 | } 70 | -------------------------------------------------------------------------------- /web/src/main/webapp/WEB-INF/views/include/footer.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> 2 | <%@ taglib prefix="c" uri="jakarta.tags.core" %> 3 | <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 4 | 5 | 6 | 7 | 42 | -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/contests/contest.css: -------------------------------------------------------------------------------- 1 | div#content div#main-content div.contest { 2 | -webkit-box-shadow: 0 0 5px rgba(0,0,0,0.1); 3 | -moz-box-shadow: 0 0 5px rgba(0,0,0,0.1); 4 | box-shadow: 0 0 5px rgba(0,0,0,0.1); 5 | } 6 | 7 | div#content div#main-content div.contest div.header { 8 | background-color: #16a085; 9 | color: #fff; 10 | padding: 10px; 11 | } 12 | 13 | div#content div#main-content div.contest div.body { 14 | background-color: #fff; 15 | position: relative; 16 | padding: 20px; 17 | } 18 | 19 | div#content div#main-content div.contest div.body div.section { 20 | border-top: 5px solid #16a085; 21 | padding-top: 20px; 22 | margin-top: 20px; 23 | } 24 | 25 | div#content div#main-content div.contest div.body div.section:first-child { 26 | border-top: none; 27 | padding-top: 0; 28 | margin-top: 0; 29 | } 30 | 31 | div#content div#main-content div.contest div.body table#problems { 32 | border-bottom: 1px solid #ddd; 33 | margin-bottom: 10px; 34 | } 35 | 36 | div#content div#main-content div.contest div.body table#problems td.flag-PD > a, 37 | div#content div#main-content div.contest div.body table#problems td.flag-CPL > a, 38 | div#content div#main-content div.contest div.body table#problems td.flag-JUD > a { 39 | color: #ffa500; 40 | } 41 | 42 | div#content div#main-content div.contest div.body table#problems td.flag-AC > a { 43 | color: #f00; 44 | } 45 | 46 | div#content div#main-content div.contest div.body table#problems td.flag-PE > a, 47 | div#content div#main-content div.contest div.body table#problems td.flag-WA > a, 48 | div#content div#main-content div.contest div.body table#problems td.flag-TLE > a, 49 | div#content div#main-content div.contest div.body table#problems td.flag-MLE > a, 50 | div#content div#main-content div.contest div.body table#problems td.flag-OLE > a { 51 | color: #4d7aff; 52 | } 53 | 54 | div#content div#main-content div.contest div.body table#problems td.flag-RE > a, 55 | div#content div#main-content div.contest div.body table#problems td.flag-CE > a { 56 | color: #49a932; 57 | } 58 | 59 | div#content div#main-content div.contest div.body table#problems td.flag-SE > a { 60 | color: #34495e; 61 | } 62 | 63 | div#content div#sidebar div.section { 64 | background-color: #fff; 65 | border-top: 4px solid #16a085; 66 | margin-bottom: 20px; 67 | padding: 10px; 68 | } 69 | 70 | div#content div#sidebar div.section ul { 71 | list-style: none; 72 | margin-left: 0; 73 | } 74 | 75 | div#content div#sidebar div#basic-info div.span5, 76 | div#content div#sidebar div#basic-info div.span7 { 77 | min-height: 24px; 78 | } 79 | 80 | div#content div#sidebar div#basic-info div.Live { 81 | color: #ff8080; 82 | } 83 | 84 | div#content div#sidebar div#basic-info div.Done { 85 | color: #444; 86 | } 87 | 88 | div#content div#sidebar div#basic-info div.Ready { 89 | color: #16a085; 90 | } 91 | -------------------------------------------------------------------------------- /web/src/main/java/org/verwandlung/voj/web/mapper/EmailValidationMapper.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.web.mapper; 41 | 42 | import org.apache.ibatis.annotations.CacheNamespace; 43 | import org.apache.ibatis.annotations.Param; 44 | 45 | import org.verwandlung.voj.web.model.EmailValidation; 46 | 47 | /** 48 | * EmailValidation Data Access Object. 49 | * 50 | * @author Haozhe Xie 51 | */ 52 | @CacheNamespace(implementation = org.mybatis.caches.ehcache.EhcacheCache.class) 53 | public interface EmailValidationMapper { 54 | /** 55 | * 获取某个电子邮件地址对应的EmailValidation对象. 56 | * 57 | * @param email - 电子邮件地址 58 | * @return 对应的EmailValidation对象 59 | */ 60 | EmailValidation getEmailValidation(@Param("email") String email); 61 | 62 | /** 63 | * 创建新的电子邮件验证凭据. 64 | * 65 | * @param emailValidation - 电子邮件验证凭据 66 | */ 67 | int createEmailValidation(EmailValidation emailValidation); 68 | 69 | /** 70 | * 删除电子邮件验证凭据. 71 | * 72 | * @param email - 电子邮件地址 73 | */ 74 | int deleteEmailValidation(@Param("email") String email); 75 | } 76 | -------------------------------------------------------------------------------- /web/src/main/java/org/verwandlung/voj/web/util/HtmlTextFilter.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.web.util; 41 | 42 | import org.jsoup.Jsoup; 43 | import org.jsoup.nodes.Document; 44 | import org.jsoup.safety.Safelist; 45 | 46 | /** 47 | * HTML文本过滤组件. 48 | * 49 | * @author Haozhe Xie 50 | */ 51 | public class HtmlTextFilter { 52 | /** Utility classes should not have a public constructor. */ 53 | private HtmlTextFilter() {} 54 | 55 | /** 56 | * 过滤包含HTML字符串. 57 | * 58 | * @param text - 待过滤的字符串 59 | * @return 过滤后的字符串. 60 | */ 61 | public static String filter(String text) { 62 | if (text == null) { 63 | return text; 64 | } 65 | 66 | Document document = Jsoup.parse(text); 67 | document.outputSettings(new Document.OutputSettings().prettyPrint(false)); 68 | document.select("br").append("\\n"); 69 | document.select("p").prepend("\\n\\n"); 70 | String s = document.html().replaceAll("\\\\n", "\n"); 71 | return Jsoup.clean(s, "", Safelist.none(), new Document.OutputSettings().prettyPrint(false)); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /web/src/main/java/org/verwandlung/voj/web/mapper/UserGroupMapper.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.web.mapper; 41 | 42 | import java.util.List; 43 | 44 | import org.apache.ibatis.annotations.CacheNamespace; 45 | import org.apache.ibatis.annotations.Param; 46 | 47 | import org.verwandlung.voj.web.model.UserGroup; 48 | 49 | /** 50 | * UserGroup Data Access Object. 51 | * 52 | * @author Haozhe Xie 53 | */ 54 | @CacheNamespace(implementation = org.mybatis.caches.ehcache.EhcacheCache.class) 55 | public interface UserGroupMapper { 56 | /** 57 | * 获取全部的用户组对象. 58 | * 59 | * @return 全部的用户组对象的列表 60 | */ 61 | List getUserGroups(); 62 | 63 | /** 64 | * 通过用户组的唯一标识符获取用户组对象. 65 | * 66 | * @param userGroupId - 用户组的唯一标识符 67 | * @return 预期的用户组对象或空引用 68 | */ 69 | UserGroup getUserGroupUsingId(@Param("userGroupId") int userGroupId); 70 | 71 | /** 72 | * 通过用户组的别名获取用户组对象. 73 | * 74 | * @param userGroupSlug - 用户组的别名 75 | * @return 预期的用户组对象或空引用 76 | */ 77 | UserGroup getUserGroupUsingSlug(@Param("userGroupSlug") String userGroupSlug); 78 | } 79 | -------------------------------------------------------------------------------- /web/src/main/webapp/assets/css/administration/problem-categories.css: -------------------------------------------------------------------------------- 1 | form#new-category-form p { 2 | color: #666; 3 | font-size: 12px; 4 | margin: 5px 0; 5 | } 6 | 7 | form#new-category-form select { 8 | margin-bottom: 0; 9 | } 10 | 11 | form#new-category-form button { 12 | margin-top: 20px; 13 | } 14 | 15 | table#problem-categories label.checkbox { 16 | margin: 0; 17 | } 18 | 19 | div#filters { 20 | margin-bottom: 20px; 21 | } 22 | 23 | div#filters select#actions { 24 | margin: 0; 25 | width: 40%; 26 | } 27 | 28 | table#problem-categories { 29 | border: 1px solid #e8e8e8; 30 | -webkit-box-shadow: 0 0 5px rgba(0,0,0,0.1); 31 | -moz-box-shadow: 0 0 5px rgba(0,0,0,0.1); 32 | box-shadow: 0 0 5px rgba(0,0,0,0.1); 33 | } 34 | 35 | table#problem-categories thead { 36 | background-color: #16a085; 37 | color: #fff; 38 | } 39 | 40 | table#problem-categories tfoot { 41 | background-color: #fff; 42 | color: #666; 43 | } 44 | 45 | table#problem-categories tbody td { 46 | line-height: 1.5em; 47 | } 48 | 49 | table#problem-categories ul.actions { 50 | font-size: 13px; 51 | margin: 0; 52 | visibility: hidden; 53 | } 54 | 55 | table#problem-categories ul.actions li { 56 | padding-left: 0; 57 | } 58 | 59 | table#problem-categories ul.actions a { 60 | text-decoration: none; 61 | } 62 | 63 | table#problem-categories ul.actions a.action-delete { 64 | color: #e74c3c; 65 | } 66 | 67 | table#problem-categories tr.edit-fieldset select#category-parent-edit { 68 | margin-bottom: 0; 69 | } 70 | 71 | table#problem-categories tr.edit-fieldset p#category-name-error-message, 72 | table#problem-categories tr.edit-fieldset p#category-slug-error-message, 73 | table#problem-categories tr.edit-fieldset p#category-parent-error-message { 74 | color: #e74c3c; 75 | font-size: 12px; 76 | margin: 5px 0 0; 77 | } 78 | 79 | table#problem-categories tr.edit-fieldset div#edit-fieldset-controls { 80 | margin-top: 20px; 81 | } 82 | 83 | table#problem-categories tr.edit-fieldset input[type=text].error, 84 | table#problem-categories tr.edit-fieldset select.error { 85 | border-color: #e74c3c; 86 | color: #e74c3c; 87 | } 88 | 89 | /* Smaller than standard 960 (devices and browsers) */ 90 | @media only screen and (max-width: 959px) { 91 | } 92 | 93 | /* Tablet Portrait size to standard 960 (devices and browsers) */ 94 | @media only screen and (min-width: 768px) and (max-width: 959px) { 95 | } 96 | 97 | /* All Mobile Sizes (devices and browser) */ 98 | @media only screen and (max-width: 767px) { 99 | } 100 | 101 | /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ 102 | @media only screen and (min-width: 480px) and (max-width: 767px) { 103 | } 104 | 105 | /* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ 106 | @media only screen and (max-width: 479px) { 107 | } -------------------------------------------------------------------------------- /web/src/main/java/org/verwandlung/voj/web/mapper/OptionMapper.java: -------------------------------------------------------------------------------- 1 | /* Verwandlung Online Judge - A cross-platform judge online system 2 | * Copyright (C) 2018 Haozhe Xie 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * 18 | * _ooOoo_ 19 | * o8888888o 20 | * 88" . "88 21 | * (| -_- |) 22 | * O\ = /O 23 | * ____/`---'\____ 24 | * .' \\| |// `. 25 | * / \\||| : |||// \ 26 | * / _||||| -:- |||||- \ 27 | * | | \\\ - /// | | 28 | * | \_| ''\---/'' | | 29 | * \ .-\__ `-` ___/-. / 30 | * ___`. .' /--.--\ `. . __ 31 | * ."" '< `.___\_<|>_/___.' >'"". 32 | * | | : `- \`.;`\ _ /`;.`/ - ` : | | 33 | * \ \ `-. \_ __\ /__ _/ .-` / / 34 | * ======`-.____`-.___\_____/___.-`____.-'====== 35 | * `=---=' 36 | * 37 | * HERE BE BUDDHA 38 | * 39 | */ 40 | package org.verwandlung.voj.web.mapper; 41 | 42 | import java.util.List; 43 | 44 | import org.apache.ibatis.annotations.CacheNamespace; 45 | import org.apache.ibatis.annotations.Param; 46 | 47 | import org.verwandlung.voj.web.model.Option; 48 | 49 | /** 50 | * Opton Data Access Object. 51 | * 52 | * @author Haozhe Xie 53 | */ 54 | @CacheNamespace(implementation = org.mybatis.caches.ehcache.EhcacheCache.class) 55 | public interface OptionMapper { 56 | /** 57 | * 获取全部系统选项. 58 | * 59 | * @return 一个包含全部系统选项的列表 60 | */ 61 | List