diff --git a/.github/workflows/autoconf-check-different-distro.yml b/.github/workflows/autoconf-check-different-distro.yml index 8216eb3a26..9db54dfaa8 100644 --- a/.github/workflows/autoconf-check-different-distro.yml +++ b/.github/workflows/autoconf-check-different-distro.yml @@ -20,7 +20,7 @@ jobs: image: ${{ matrix.os }}:${{ matrix.version }} steps: - name: Install git so we get the .github directory - run: dnf install -y git + run: dnf install -y git composer - uses: actions/checkout@v4 - name: Setup image and run bats tests run: .github/jobs/configure-checks/setup_configure_image.sh diff --git a/.github/workflows/autoconf-check.yml b/.github/workflows/autoconf-check.yml index 016ec024f7..58f6f691d1 100644 --- a/.github/workflows/autoconf-check.yml +++ b/.github/workflows/autoconf-check.yml @@ -32,7 +32,7 @@ jobs: image: ${{ matrix.os }}:${{ matrix.version }} steps: - name: Install git so we get the .github directory - run: apt-get update; apt-get install -y git + run: apt-get update; apt-get install -y git composer - uses: actions/checkout@v4 - name: Setup image and run bats tests run: .github/jobs/configure-checks/setup_configure_image.sh diff --git a/.github/workflows/mayhem-api-template.yml b/.github/workflows/mayhem-api-template.yml deleted file mode 100644 index 9556bb3718..0000000000 --- a/.github/workflows/mayhem-api-template.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: "Mayhem API analysis (Template)" - -on: - workflow_call: - inputs: - version: - required: true - type: string - duration: - required: true - type: string - secrets: - MAPI_TOKEN: - required: true - -jobs: - mayhem: - name: Mayhem API analysis - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - env: - DB_DATABASE: domjudge - DB_USER: user - DB_PASSWORD: password - steps: - - uses: actions/checkout@v4 - - - name: Install DOMjudge - run: .github/jobs/baseinstall.sh ${{ inputs.version }} - - - name: Dump the OpenAPI - run: .github/jobs/getapi.sh - - - uses: actions/upload-artifact@v3 - if: ${{ inputs.version == 'guest' }} - with: - name: all-apispec - path: | - /home/runner/work/domjudge/domjudge/openapi.json - - - name: Mayhem for API - uses: ForAllSecure/mapi-action@v1 - if: ${{ inputs.version == 'guest' }} - continue-on-error: true - with: - mapi-token: ${{ secrets.MAPI_TOKEN }} - api-url: http://localhost/domjudge - api-spec: http://localhost/domjudge/api/doc.json # swagger/openAPI doc hosted here - duration: "auto" # Only spend time if we need to recheck issues from last time or find issues - sarif-report: mapi.sarif - run-args: | - --config - .github/jobs/data/mapi.config - --ignore-endpoint - ".*strict=true.*" - --ignore-endpoint - ".*strict=True.*" - - - name: Mayhem for API (For application role) - uses: ForAllSecure/mapi-action@v1 - if: ${{ inputs.version != 'guest' }} - continue-on-error: true - with: - mapi-token: ${{ secrets.MAPI_TOKEN }} - target: domjudge-${{ inputs.version }} - api-url: http://localhost/domjudge - api-spec: http://localhost/domjudge/api/doc.json # swagger/openAPI doc hosted here - duration: "${{ inputs.duration }}" - sarif-report: mapi.sarif - run-args: | - --config - .github/jobs/data/mapi.config - --basic-auth - admin:password - --ignore-endpoint - ".*strict=true.*" - --ignore-endpoint - ".*strict=True.*" - - - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: mapi.sarif - - - uses: actions/upload-artifact@v3 - with: - name: ${{ inputs.version }}-logs - path: | - /var/log/nginx - /opt/domjudge/domserver/webapp/var/log/*.log diff --git a/.github/workflows/mayhem-daily.yml b/.github/workflows/mayhem-daily.yml deleted file mode 100644 index 2118bf6920..0000000000 --- a/.github/workflows/mayhem-daily.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: "Mayhem API daily (admin role only)" - -on: - schedule: - - cron: '0 23 * * *' - -jobs: - mayhem-template: - uses: ./.github/workflows/mayhem-api-template.yml - with: - version: "admin" - duration: "auto" - secrets: - MAPI_TOKEN: ${{ secrets.MAPI_TOKEN }} diff --git a/.github/workflows/mayhem-weekly.yml b/.github/workflows/mayhem-weekly.yml deleted file mode 100644 index 71cc90ecba..0000000000 --- a/.github/workflows/mayhem-weekly.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: "Mayhem API weekly (all roles)" - -on: - schedule: - - cron: '0 23 * * 0' - -jobs: - mayhem-template: - strategy: - matrix: - include: - - version: "team" - duration: "5m" - - version: "guest" - duration: "auto" - - version: "jury" - duration: "5min" - - version: "admin" - duration: "10m" - uses: ./.github/workflows/mayhem-api-template.yml - with: - version: "${{ matrix.version }}" - duration: "${{ matrix.duration }}" - secrets: - MAPI_TOKEN: ${{ secrets.MAPI_TOKEN }} diff --git a/ChangeLog b/ChangeLog index 30ebc035f2..5bc6199478 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ DOMjudge Programming Contest Judging System +Version 8.3.1 - 21 July 2024 + - Create autoload_runtime.php as normal user to prevent a composer warning. + Version 8.3.0 - 31 May 2024 --------------------------- - [security] Close metadata file descriptor for the child in runguard. diff --git a/Makefile b/Makefile index df4f2069a0..1f8bea7047 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ endif domserver: domserver-configure paths.mk config judgehost: judgehost-configure paths.mk config docs: paths.mk config -install-domserver: domserver composer-dump-autoload domserver-create-dirs +install-domserver: domserver domserver-create-dirs install-judgehost: judgehost judgehost-create-dirs install-docs: docs-create-dirs dist: configure composer-dependencies @@ -76,12 +76,6 @@ endif composer-dependencies-dev: composer $(subst 1,-q,$(QUIET)) install --prefer-dist --no-scripts --no-plugins -# Dump autoload dependencies (including plugins) -# This is needed since symfony/runtime is a Composer plugin that runs while dumping -# the autoload file -composer-dump-autoload: - composer $(subst 1,-q,$(QUIET)) dump-autoload -o -a - composer-dump-autoload-dev: composer $(subst 1,-q,$(QUIET)) dump-autoload @@ -101,7 +95,7 @@ build-scripts: # List of SUBDIRS for recursive targets: build: SUBDIRS= lib misc-tools -domserver: SUBDIRS=etc sql misc-tools webapp +domserver: SUBDIRS=etc lib sql misc-tools webapp install-domserver: SUBDIRS=etc lib sql misc-tools webapp example_problems judgehost: SUBDIRS=etc judge misc-tools install-judgehost: SUBDIRS=etc lib judge misc-tools @@ -222,7 +216,7 @@ webapp/.env.local: # symlinks where necessary to let it work from the source tree. # This stuff is a hack! maintainer-install: inplace-install composer-dump-autoload-dev -inplace-install: build composer-dump-autoload domserver-create-dirs judgehost-create-dirs +inplace-install: build domserver-create-dirs judgehost-create-dirs inplace-install-l: # Replace libjudgedir with symlink to prevent lots of symlinks: -rmdir $(judgehost_libjudgedir) @@ -341,5 +335,5 @@ clean-autoconf: $(addprefix inplace-,conf conf-common install uninstall) \ $(addprefix maintainer-,conf install) clean-autoconf config distdocs \ composer-dependencies composer-dependencies-dev \ - composer-dump-autoload composer-dump-autoload-dev \ + composer-dump-autoload-dev \ coverity-conf coverity-build diff --git a/README.md b/README.md index 5499da3e0a..eaa23881ee 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ DOMjudge [![Coverity Scan Status](https://img.shields.io/coverity/scan/671.svg)](https://scan.coverity.com/projects/domjudge) [![CodeQL alerts](https://github.com/DOMjudge/domjudge/actions/workflows/codeql-analysis.yml/badge.svg?branch=main&event=push)](https://github.com/DOMjudge/domjudge/actions/workflows/codeql-analysis.yml) -This is the Programming Contest Jury System "DOMjudge" version 8.3.0 +This is the Programming Contest Jury System "DOMjudge" version 8.3.1 DOMjudge is a system for running a programming contest, like the ICPC regional and world championship programming contests. diff --git a/lib/Makefile b/lib/Makefile index 5f2e2dea76..24cd0f82de 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,6 +1,9 @@ ifndef TOPDIR TOPDIR=.. endif + +REC_TARGETS = domserver + include $(TOPDIR)/Makefile.global OBJECTS = $(addsuffix $(OBJEXT),lib.error lib.misc) @@ -31,3 +34,5 @@ install-domserver: install-judgehost: $(INSTALL_DATA) -t $(DESTDIR)$(judgehost_libdir) *.php *.sh $(INSTALL_PROG) -t $(DESTDIR)$(judgehost_libdir) alert + +domserver: SUBDIRS=vendor diff --git a/lib/vendor/Makefile b/lib/vendor/Makefile new file mode 100644 index 0000000000..b6ee26a361 --- /dev/null +++ b/lib/vendor/Makefile @@ -0,0 +1,12 @@ +ifndef TOPDIR +TOPDIR=../.. +endif +include $(TOPDIR)/Makefile.global + +clean-l: + rm -f autoload_runtime.php + +autoload_runtime.php: + composer $(subst 1,-q,$(QUIET)) dump-autoload -o -a -d $(TOPDIR) + +domserver: autoload_runtime.php diff --git a/misc-tools/import-contest.in b/misc-tools/import-contest.in index 756c03d7f0..79d6ffaa9a 100755 --- a/misc-tools/import-contest.in +++ b/misc-tools/import-contest.in @@ -149,10 +149,10 @@ if import_file('organizations', ['organizations.json']): # Also import logos if we have any # We prefer the 64x64 logo. If it doesn't exist, accept a generic logo (which might be a SVG) # We also prefer PNG/SVG before JPG - import_images('organizations', 'logo', ['^logo\.64x\d+\.png$', '^logo\.(png|svg)$', '^logo\.64x\d+\.jpg$', '^logo\.jpg$']) + import_images('organizations', 'logo', ['^logo\\.64x\\d+\\.png$', '^logo\\.(png|svg)$', '^logo\\.64x\\d+\\.jpg$', '^logo\\.jpg$']) if import_file('teams', ['teams.json', 'teams2.tsv']): # Also import photos if we have any, but prefer JPG over SVG and PNG - import_images('teams', 'photo', ['^photo\.jpg$', '^photo\.(png|svg)$']) + import_images('teams', 'photo', ['^photo\\.jpg$', '^photo\\.(png|svg)$']) import_file('accounts', ['accounts.json', 'accounts.yaml', 'accounts.tsv']) problems_imported = False diff --git a/webapp/src/Controller/Jury/TeamCategoryController.php b/webapp/src/Controller/Jury/TeamCategoryController.php index 2cdba5b922..0cc075696c 100644 --- a/webapp/src/Controller/Jury/TeamCategoryController.php +++ b/webapp/src/Controller/Jury/TeamCategoryController.php @@ -250,7 +250,7 @@ public function requestRemainingRunsWholeTeamCategoryAction(string $categoryId): ->join('t.category', 'tc') ->andWhere('j.valid = true') ->andWhere('j.result != :compiler_error') - ->andWhere('tc.category = :categoryId') + ->andWhere('tc.categoryid = :categoryId') ->setParameter('compiler_error', 'compiler-error') ->setParameter('categoryId', $categoryId); if ($contestId > -1) { diff --git a/webapp/src/Controller/Team/MiscController.php b/webapp/src/Controller/Team/MiscController.php index 648b4bf727..e714d6737b 100644 --- a/webapp/src/Controller/Team/MiscController.php +++ b/webapp/src/Controller/Team/MiscController.php @@ -95,12 +95,10 @@ public function homeAction(Request $request): Response $clarifications = $this->em->createQueryBuilder() ->from(Clarification::class, 'c') ->leftJoin('c.problem', 'p') - ->leftJoin('p.contest_problems', 'cp') ->leftJoin('c.sender', 's') ->leftJoin('c.recipient', 'r') - ->select('c', 'cp', 'p') + ->select('c', 'p') ->andWhere('c.contest = :contest') - ->andWhere('cp.contest = :contest') ->andWhere('c.sender IS NULL') ->andWhere('c.recipient = :team OR c.recipient IS NULL') ->setParameter('contest', $contest) @@ -114,12 +112,10 @@ public function homeAction(Request $request): Response $clarificationRequests = $this->em->createQueryBuilder() ->from(Clarification::class, 'c') ->leftJoin('c.problem', 'p') - ->leftJoin('p.contest_problems', 'cp') ->leftJoin('c.sender', 's') ->leftJoin('c.recipient', 'r') - ->select('c', 'cp', 'p') + ->select('c', 'p') ->andWhere('c.contest = :contest') - ->andWhere('cp.contest = :contest') ->andWhere('c.sender = :team') ->setParameter('contest', $contest) ->setParameter('team', $team) diff --git a/webapp/src/DataTransferObject/Shadowing/ProblemEvent.php b/webapp/src/DataTransferObject/Shadowing/ProblemEvent.php index 2aa47e6e27..7c5b76d348 100644 --- a/webapp/src/DataTransferObject/Shadowing/ProblemEvent.php +++ b/webapp/src/DataTransferObject/Shadowing/ProblemEvent.php @@ -7,7 +7,7 @@ class ProblemEvent implements EventData public function __construct( public readonly string $id, public readonly string $name, - public readonly int $timeLimit, + public readonly float $timeLimit, public readonly ?string $label, public readonly ?string $rgb, ) {} diff --git a/webapp/src/Entity/Clarification.php b/webapp/src/Entity/Clarification.php index ed62d6013f..b52765b218 100644 --- a/webapp/src/Entity/Clarification.php +++ b/webapp/src/Entity/Clarification.php @@ -236,6 +236,14 @@ public function getProblem(): ?Problem return $this->problem; } + public function getContestProblem(): ?ContestProblem + { + if (!$this->problem) { + return null; + } + return $this->contest->getContestProblem($this->problem); + } + #[OA\Property(nullable: true)] #[Serializer\VirtualProperty] #[Serializer\SerializedName('problem_id')] diff --git a/webapp/src/Entity/Contest.php b/webapp/src/Entity/Contest.php index 0b88932cd6..9c5cfe6ab3 100644 --- a/webapp/src/Entity/Contest.php +++ b/webapp/src/Entity/Contest.php @@ -913,6 +913,16 @@ public function getProblems(): Collection return $this->problems; } + public function getContestProblem(Problem $problem): ?ContestProblem + { + foreach ($this->getProblems() as $contestProblem) { + if ($contestProblem->getProblem() === $problem) { + return $contestProblem; + } + } + return null; + } + public function addClarification(Clarification $clarification): Contest { $this->clarifications[] = $clarification; diff --git a/webapp/src/Twig/TwigExtension.php b/webapp/src/Twig/TwigExtension.php index 7238f43682..deff4a4f9c 100644 --- a/webapp/src/Twig/TwigExtension.php +++ b/webapp/src/Twig/TwigExtension.php @@ -108,7 +108,7 @@ public function getFilters(): array new TwigFilter('tsvField', $this->toTsvField(...)), new TwigFilter('fileTypeIcon', $this->fileTypeIcon(...)), new TwigFilter('problemBadge', $this->problemBadge(...), ['is_safe' => ['html']]), - new TwigFilter('problemBadgeForProblemAndContest', $this->problemBadgeForProblemAndContest(...), ['is_safe' => ['html']]), + new TwigFilter('problemBadgeForContest', $this->problemBadgeForContest(...), ['is_safe' => ['html']]), new TwigFilter('printMetadata', $this->printMetadata(...), ['is_safe' => ['html']]), new TwigFilter('printWarningContent', $this->printWarningContent(...), ['is_safe' => ['html']]), new TwigFilter('entityIdBadge', $this->entityIdBadge(...), ['is_safe' => ['html']]), @@ -1093,14 +1093,11 @@ public function problemBadge(ContestProblem $problem): string ); } - public function problemBadgeForProblemAndContest(Problem $problem, ?Contest $contest): string + public function problemBadgeForContest(Problem $problem, ?Contest $contest = null): string { - foreach ($problem->getContestProblems() as $contestProblem) { - if ($contestProblem->getContest() === $contest) { - return $this->problemBadge($contestProblem); - } - } - return ''; + $contest ??= $this->dj->getCurrentContest(); + $contestProblem = $contest?->getContestProblem($problem); + return $contestProblem === null ? '' : $this->problemBadge($contestProblem); } public function printMetadata(?string $metadata): string diff --git a/webapp/templates/jury/executable.html.twig b/webapp/templates/jury/executable.html.twig index 95cbe3956e..0cf94b261c 100644 --- a/webapp/templates/jury/executable.html.twig +++ b/webapp/templates/jury/executable.html.twig @@ -48,14 +48,14 @@ {% if executable.type == 'compare' %} {% for problem in executable.problemsCompare %} - p{{ problem.probid }} {{ problem | problemBadgeForProblemAndContest(current_contest) }} + p{{ problem.probid }} {{ problem | problemBadgeForContest }} {% set used = true %} {% endfor %} {% elseif executable.type == 'run' %} {% for problem in executable.problemsRun %} - p{{ problem.probid }} {{ problem | problemBadgeForProblemAndContest(current_contest) }} + p{{ problem.probid }} {{ problem | problemBadgeForContest }} {% set used = true %} {% endfor %} diff --git a/webapp/templates/jury/partials/clarification_list.html.twig b/webapp/templates/jury/partials/clarification_list.html.twig index 337499bb1c..09874023be 100644 --- a/webapp/templates/jury/partials/clarification_list.html.twig +++ b/webapp/templates/jury/partials/clarification_list.html.twig @@ -71,7 +71,7 @@ {%- if clarification.problem -%} - problem {{ clarification.problem.contestProblems.first | problemBadge -}} + problem {{ clarification.contestProblem | problemBadge -}} {%- elseif clarification.category -%} {{- categories[clarification.category]|default('general') -}} {%- else -%} diff --git a/webapp/templates/partials/problem_list.html.twig b/webapp/templates/partials/problem_list.html.twig index f00ef14482..0733241ddd 100644 --- a/webapp/templates/partials/problem_list.html.twig +++ b/webapp/templates/partials/problem_list.html.twig @@ -94,7 +94,7 @@ data-bs-toggle="tooltip" data-placement="top" data-html="true" - title="Between {{ stat.start.timestamp | printtime(null, contest) }} and {{ stat.end.timestamp | printtime(null, contest) }}:
{{ label }}"> + title="Between {{ stat.start.timestamp | printtime(null, contest) }} and {{ stat.end.timestamp | printtime(null, contest) }}:{{ '\n' }}{{ label }}"> {% endfor %} diff --git a/webapp/templates/team/partials/clarification.html.twig b/webapp/templates/team/partials/clarification.html.twig index eda98ce293..5004e4c0d4 100644 --- a/webapp/templates/team/partials/clarification.html.twig +++ b/webapp/templates/team/partials/clarification.html.twig @@ -4,7 +4,7 @@
Subject: {% if clarification.problem %} - Problem {{ clarification.problem.contestProblems.first.shortname }}: {{ clarification.problem.name }} + Problem {{ clarification.contestProblem.shortname }}: {{ clarification.problem.name }} {% elseif clarification.category %} {{ categories[clarification.category]|default('general') }} {% else %} diff --git a/webapp/templates/team/partials/clarification_list.html.twig b/webapp/templates/team/partials/clarification_list.html.twig index a36c1e0f30..47b154c542 100644 --- a/webapp/templates/team/partials/clarification_list.html.twig +++ b/webapp/templates/team/partials/clarification_list.html.twig @@ -44,7 +44,7 @@ {%- if clarification.problem -%} - problem {{ clarification.problem.contestProblems.first | problemBadge -}} + problem {{ clarification.contestProblem | problemBadge -}} {%- elseif clarification.category -%} {{- categories[clarification.category]|default('general') -}} {%- else -%}