Skip to content

Commit e391fd5

Browse files
committed
Use proper buttons and route to start/delay/freeze/unfreeze/... contests.
This will be helpful when you want to add the same functionality to the individual `contest` page as well.
1 parent e711d60 commit e391fd5

File tree

3 files changed

+139
-152
lines changed

3 files changed

+139
-152
lines changed

gitlab/integration.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ set -x
239239
# Finalize contest so that awards appear in the feed; first freeze and end the
240240
# contest if that has not already been done.
241241
export CURLOPTS="--fail -m 30 -b $COOKIEJAR"
242-
curl $CURLOPTS -X POST -d 'contest=1&donow[freeze]=freeze now' http://localhost/domjudge/jury/contests || true
243-
curl $CURLOPTS -X POST -d 'contest=1&donow[end]=end now' http://localhost/domjudge/jury/contests || true
242+
curl $CURLOPTS http://localhost/domjudge/jury/contests/1/freeze/doNow || true
243+
curl $CURLOPTS http://localhost/domjudge/jury/contests/1/end/doNow || true
244244
curl $CURLOPTS -X POST -d 'finalize_contest[b]=0&finalize_contest[finalizecomment]=gitlab&finalize_contest[finalize]=' http://localhost/domjudge/jury/contests/1/finalize
245245

246246
# shellcheck disable=SC2002,SC2196

webapp/src/Controller/Jury/ContestController.php

Lines changed: 96 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -70,106 +70,6 @@ public function indexAction(Request $request): Response
7070
{
7171
$em = $this->em;
7272

73-
if ($doNow = $request->request->all('donow')) {
74-
$times = ['activate', 'start', 'freeze', 'end',
75-
'unfreeze', 'finalize', 'deactivate'];
76-
$start_actions = ['delay_start', 'resume_start'];
77-
$actions = array_merge($times, $start_actions);
78-
79-
if (!$this->isGranted('ROLE_ADMIN')) {
80-
throw new AccessDeniedHttpException();
81-
}
82-
$contest = $em->getRepository(Contest::class)->find($request->request->get('contest'));
83-
if (!$contest) {
84-
throw new NotFoundHttpException('Contest not found');
85-
}
86-
87-
$time = key($doNow);
88-
if (!in_array($time, $actions, true)) {
89-
throw new BadRequestHttpException(
90-
sprintf("Unknown value '%s' for timetype", $time)
91-
);
92-
}
93-
94-
if ($time === 'finalize') {
95-
return $this->redirectToRoute(
96-
'jury_contest_finalize',
97-
['contestId' => $contest->getCid()]
98-
);
99-
}
100-
101-
$now = (int)floor(Utils::now());
102-
$nowstring = date('Y-m-d H:i:s ', $now) . date_default_timezone_get();
103-
$this->dj->auditlog('contest', $contest->getCid(), $time . ' now', $nowstring);
104-
105-
// Special case delay/resume start (only sets/unsets starttime_undefined).
106-
$maxSeconds = Contest::STARTTIME_UPDATE_MIN_SECONDS_BEFORE;
107-
if (in_array($time, $start_actions, true)) {
108-
$enabled = $time !== 'delay_start';
109-
if (Utils::difftime((float)$contest->getStarttime(false), $now) <= $maxSeconds) {
110-
$this->addFlash(
111-
'error',
112-
sprintf("Cannot %s less than %d seconds before contest start.",
113-
$time, $maxSeconds)
114-
);
115-
return $this->redirectToRoute('jury_contests');
116-
}
117-
$contest->setStarttimeEnabled($enabled);
118-
$em->flush();
119-
$this->eventLogService->log(
120-
'contest',
121-
$contest->getCid(),
122-
EventLogService::ACTION_UPDATE,
123-
$contest->getCid()
124-
);
125-
$this->addFlash('scoreboard_refresh',
126-
'After changing the contest start time, it may be ' .
127-
'necessary to recalculate any cached scoreboards.');
128-
return $this->redirectToRoute('jury_contests');
129-
}
130-
131-
$juryTimeData = $contest->getDataForJuryInterface();
132-
if (!$juryTimeData[$time]['show_button']) {
133-
throw new BadRequestHttpException(
134-
sprintf('Cannot update %s time at this moment', $time)
135-
);
136-
}
137-
138-
// starttime is special because other, relative times depend on it.
139-
if ($time == 'start') {
140-
if ($contest->getStarttimeEnabled() &&
141-
Utils::difftime((float)$contest->getStarttime(false),
142-
$now) <= $maxSeconds) {
143-
$this->addFlash(
144-
'danger',
145-
sprintf("Cannot update starttime less than %d seconds before contest start.",
146-
$maxSeconds)
147-
);
148-
return $this->redirectToRoute('jury_contests');
149-
}
150-
$contest
151-
->setStarttime($now)
152-
->setStarttimeString($nowstring)
153-
->setStarttimeEnabled(true);
154-
$em->flush();
155-
156-
$this->addFlash('scoreboard_refresh',
157-
'After changing the contest start time, it may be ' .
158-
'necessary to recalculate any cached scoreboards.');
159-
} else {
160-
$method = sprintf('set%stimeString', $time);
161-
$contest->{$method}($nowstring);
162-
$em->flush();
163-
}
164-
$this->eventLogService->log(
165-
'contest',
166-
$contest->getCid(),
167-
EventLogService::ACTION_UPDATE,
168-
$contest->getCid()
169-
);
170-
return $this->redirectToRoute('jury_contests');
171-
}
172-
17373
/** @var Contest[] $contests */
17474
$contests = $em->createQueryBuilder()
17575
->select('c')
@@ -872,6 +772,102 @@ public function finalizeAction(Request $request, int $contestId): Response
872772
]);
873773
}
874774

775+
#[IsGranted('ROLE_ADMIN')]
776+
#[Route(path: '/{contestId<\d+>}/{time}/doNow', name: 'jury_contest_donow')]
777+
public function doNowAction(Request $request, int $contestId, string $time): Response
778+
{
779+
$times = ['activate', 'start', 'freeze', 'end', 'unfreeze', 'finalize', 'deactivate'];
780+
$start_actions = ['delay_start', 'resume_start'];
781+
$actions = array_merge($times, $start_actions);
782+
783+
$contest = $this->em->getRepository(Contest::class)->find($contestId);
784+
if (!$contest) {
785+
throw new NotFoundHttpException(sprintf('Contest with ID %s not found', $contestId));
786+
}
787+
788+
if (!in_array($time, $actions, true)) {
789+
throw new BadRequestHttpException(sprintf("Unknown value '%s' for timetype", $time));
790+
}
791+
792+
if ($time === 'finalize') {
793+
return $this->redirectToRoute('jury_contest_finalize', ['contestId' => $contest->getCid()]);
794+
}
795+
796+
$now = (int)floor(Utils::now());
797+
$nowstring = date('Y-m-d H:i:s ', $now) . date_default_timezone_get();
798+
$this->dj->auditlog('contest', $contest->getCid(), $time . ' now', $nowstring);
799+
800+
// Special case delay/resume start (only sets/unsets starttime_undefined).
801+
$maxSeconds = Contest::STARTTIME_UPDATE_MIN_SECONDS_BEFORE;
802+
if (in_array($time, $start_actions, true)) {
803+
$enabled = $time !== 'delay_start';
804+
if (Utils::difftime((float)$contest->getStarttime(false), $now) <= $maxSeconds) {
805+
$this->addFlash(
806+
'error',
807+
sprintf("Cannot '%s' less than %d seconds before contest start.",
808+
$time, $maxSeconds)
809+
);
810+
return $this->redirectToRoute('jury_contests');
811+
}
812+
$contest->setStarttimeEnabled($enabled);
813+
$this->em->flush();
814+
$this->eventLogService->log(
815+
'contest',
816+
$contest->getCid(),
817+
EventLogService::ACTION_UPDATE,
818+
$contest->getCid()
819+
);
820+
$this->addFlash('scoreboard_refresh', 'After changing the contest start time, it may be '
821+
. 'necessary to recalculate any cached scoreboards.');
822+
return $this->redirectToRoute('jury_contests');
823+
}
824+
825+
$juryTimeData = $contest->getDataForJuryInterface();
826+
if (!$juryTimeData[$time]['show_button']) {
827+
throw new BadRequestHttpException(
828+
sprintf("Cannot update '%s' time at this moment", $time)
829+
);
830+
}
831+
832+
// starttime is special because other, relative times depend on it.
833+
if ($time == 'start') {
834+
if ($contest->getStarttimeEnabled() &&
835+
Utils::difftime((float)$contest->getStarttime(false),
836+
$now) <= $maxSeconds) {
837+
$this->addFlash(
838+
'danger',
839+
sprintf("Cannot update starttime less than %d seconds before contest start.",
840+
$maxSeconds)
841+
);
842+
return $this->redirectToRoute('jury_contests');
843+
}
844+
$contest
845+
->setStarttime($now)
846+
->setStarttimeString($nowstring)
847+
->setStarttimeEnabled(true);
848+
$this->em->flush();
849+
850+
$this->addFlash('scoreboard_refresh', 'After changing the contest start time, it may be '
851+
. 'necessary to recalculate any cached scoreboards.');
852+
} else {
853+
$method = sprintf('set%stimeString', $time);
854+
$contest->{$method}($nowstring);
855+
$this->em->flush();
856+
}
857+
$this->eventLogService->log(
858+
'contest',
859+
$contest->getCid(),
860+
EventLogService::ACTION_UPDATE,
861+
$contest->getCid()
862+
);
863+
864+
$referer = $request->headers->get('referer');
865+
if ($referer) {
866+
return $this->redirect($referer);
867+
}
868+
return $this->redirectToRoute('jury_contests');
869+
}
870+
875871
#[Route(path: '/{contestId<\d+>}/request-remaining', name: 'jury_contest_request_remaining')]
876872
public function requestRemainingRunsWholeContestAction(int $contestId): RedirectResponse
877873
{

webapp/templates/jury/contests.html.twig

Lines changed: 41 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,57 +14,51 @@
1414
<h3>Current contests</h3>
1515

1616
{% for contest in current_contests %}
17-
{# TODO: at some point use real Symfony forms here? Is maybe hard because of all the submit buttons... #}
18-
<form action="{{ path('jury_contests') }}" method="post">
19-
<input type="hidden" name="contest" value="{{ contest.cid }}"/>
20-
<div class="row mb-4">
21-
<div class="col-lg-8">
22-
<div class="card">
23-
<div class="card-header">
24-
{{ contest.name }} ({{ contest.shortname }} - c{{ contest.cid }})
25-
{% if contest.locked %}
26-
<i class="fas fa-lock"></i>
27-
{% endif %}
28-
</div>
29-
<div class="card-body">
30-
{% if not contest.starttimeEnabled and contest.finalizetime is not empty %}
31-
<div class="alert alert-warning">
32-
<strong>Warning:</strong> start time is undefined, but contest is finalized!
33-
</div>
34-
{% endif %}
35-
<table class="table table-hover">
36-
<tbody>
37-
{% for type, data in contest.dataForJuryInterface %}
38-
<tr>
39-
<td class="{{ data.class|default('') }}">
40-
{% if data.icon is defined %}
41-
<i class="fas fa-{{ data.icon }}"></i>
17+
<div class="row mb-4">
18+
<div class="col-lg-8">
19+
<div class="card">
20+
<div class="card-header">
21+
{{ contest.name }} ({{ contest.shortname }} - c{{ contest.cid }})
22+
{% if contest.locked %}
23+
<i class="fas fa-lock"></i>
24+
{% endif %}
25+
</div>
26+
<div class="card-body">
27+
{% if not contest.starttimeEnabled and contest.finalizetime is not empty %}
28+
<div class="alert alert-warning">
29+
<strong>Warning:</strong> start time is undefined, but contest is finalized!
30+
</div>
31+
{% endif %}
32+
<table class="table table-hover">
33+
<tbody>
34+
{% for type, data in contest.dataForJuryInterface %}
35+
<tr>
36+
<td class="{{ data.class|default('') }}">
37+
{% if data.icon is defined %}
38+
<i class="fas fa-{{ data.icon }}"></i>
39+
{% endif %}
40+
</td>
41+
<td class="{{ data.class|default('') }}"><b>{{ data.label }}:</b></td>
42+
<td class="{{ data.class|default('') }}">{{ data.time }}</td>
43+
{% if is_granted('ROLE_ADMIN') %}
44+
<td>
45+
{% if data.show_button %}
46+
{% set button_label = type ~ " now" %}
47+
{{ button(path('jury_contest_donow', {'contestId': contest.cid, 'time': type}), button_label, 'primary btn-sm') }}
48+
{% endif %}
49+
{% if data.extra_button is defined %}
50+
{{ button(path('jury_contest_donow', {'contestId': contest.cid, 'time': data.extra_button.type}), data.extra_button.label, 'primary btn-sm') }}
4251
{% endif %}
4352
</td>
44-
<td class="{{ data.class|default('') }}"><b>{{ data.label }}:</b></td>
45-
<td class="{{ data.class|default('') }}">{{ data.time }}</td>
46-
{% if is_granted('ROLE_ADMIN') %}
47-
<td>
48-
{% if data.show_button %}
49-
<input type="submit" class="btn btn-primary btn-sm"
50-
name="donow[{{ type }}]" value="{{ type }} now"/>
51-
{% endif %}
52-
{% if data.extra_button is defined %}
53-
<input type="submit" class="btn btn-primary btn-sm"
54-
name="donow[{{ data.extra_button.type }}]"
55-
value="{{ data.extra_button.label }}"/>
56-
{% endif %}
57-
</td>
58-
{% endif %}
59-
</tr>
60-
{% endfor %}
61-
</tbody>
62-
</table>
63-
</div>
53+
{% endif %}
54+
</tr>
55+
{% endfor %}
56+
</tbody>
57+
</table>
6458
</div>
6559
</div>
6660
</div>
67-
</form>
61+
</div>
6862
{% else %}
6963
{% if upcoming_contest is empty %}
7064
<div class="alert alert-danger">
@@ -77,10 +71,7 @@
7771
<i>{{ upcoming_contest.name }} ({{ upcoming_contest.shortname }})</i>;
7872
active from {{ upcoming_contest.activatetime | printtime('D d M Y H:i:s T') }}
7973
</p>
80-
<form action="{{ path('jury_contests') }}" method="post">
81-
<input type="hidden" name="contest" value="{{ upcoming_contest.cid }}"/>
82-
<input type="submit" class="btn btn-primary" name="donow[activate]" value="Activate now"/>
83-
</form>
74+
{{ button(path('jury_contest_donow', {'contestId': upcoming_contest.cid, 'time': 'activate'}), 'Activate now', 'primary') }}
8475
</div>
8576
{% endif %}
8677
{% endfor %}

0 commit comments

Comments
 (0)