diff --git a/webapp/src/Controller/BaseController.php b/webapp/src/Controller/BaseController.php index 474d093d76..78eb67a4ca 100644 --- a/webapp/src/Controller/BaseController.php +++ b/webapp/src/Controller/BaseController.php @@ -372,7 +372,7 @@ protected function buildDeleteTree(array $entities, array $relations): array { } $primaryKeyData[] = $primaryKeyDataTemp; } - return [$isError, $primaryKeyData, $messages]; + return [$isError, $primaryKeyData, array_values(array_unique($messages))]; } /** @@ -445,6 +445,7 @@ protected function deleteEntities( 'showModalSubmit' => !$isError, 'modalUrl' => $request->getRequestUri(), 'redirectUrl' => $redirectUrl, + 'count' => count($entities), ]; if ($request->isXmlHttpRequest()) { return $this->render('jury/delete_modal.html.twig', $data); diff --git a/webapp/src/Controller/Jury/ProblemController.php b/webapp/src/Controller/Jury/ProblemController.php index 6fc0738aef..4cd07fee96 100644 --- a/webapp/src/Controller/Jury/ProblemController.php +++ b/webapp/src/Controller/Jury/ProblemController.php @@ -88,6 +88,13 @@ public function indexAction(): Response 'type' => ['title' => 'type', 'sort' => true], ]; + if ($this->isGranted('ROLE_ADMIN')) { + $table_fields = array_merge( + ['checkbox' => ['title' => '', 'sort' => false, 'search' => false, 'raw' => true]], + $table_fields + ); + } + $contestCountData = $this->em->createQueryBuilder() ->from(ContestProblem::class, 'cp') ->select('COUNT(cp.shortname) AS count', 'p.probid') @@ -109,6 +116,28 @@ public function indexAction(): Response $p = $row[0]; $problemdata = []; $problemactions = []; + + if ($this->isGranted('ROLE_ADMIN')) { + $problemIsLocked = false; + foreach ($p->getContestProblems() as $contestProblem) { + if ($contestProblem->getContest()->isLocked()) { + $problemIsLocked = true; + break; + } + } + + if (!$problemIsLocked) { + $problemdata['checkbox'] = [ + 'value' => sprintf( + '', + $p->getProbid() + ) + ]; + } else { + $problemdata['checkbox'] = ['value' => '']; + } + } + // Get whatever fields we can from the problem object itself. foreach ($table_fields as $k => $v) { if ($propertyAccessor->isReadable($p, $k)) { @@ -999,6 +1028,39 @@ public function editAction(Request $request, int $probId): Response ]); } + #[IsGranted('ROLE_ADMIN')] + #[Route(path: '/delete-multiple', name: 'jury_problem_delete_multiple', methods: ['GET', 'POST'])] + public function deleteMultipleAction(Request $request): Response + { + $ids = $request->query->all('ids'); + if (empty($ids)) { + throw new BadRequestHttpException('No IDs specified for deletion'); + } + + $problems = $this->em->getRepository(Problem::class)->findBy(['probid' => $ids]); + + $deletableProblems = []; + foreach ($problems as $problem) { + $isLocked = false; + foreach ($problem->getContestProblems() as $contestProblem) { + if ($contestProblem->getContest()->isLocked()) { + $isLocked = true; + break; + } + } + if (!$isLocked) { + $deletableProblems[] = $problem; + } + } + + if (empty($deletableProblems)) { + $this->addFlash('warning', 'No problems could be deleted (they might be locked).'); + return $this->redirectToRoute('jury_problems'); + } + + return $this->deleteEntities($request, $deletableProblems, $this->generateUrl('jury_problems')); + } + #[IsGranted('ROLE_ADMIN')] #[Route(path: '/{probId<\d+>}/delete', name: 'jury_problem_delete')] public function deleteAction(Request $request, int $probId): Response diff --git a/webapp/templates/jury/delete_modal.html.twig b/webapp/templates/jury/delete_modal.html.twig index a1a7e104fb..eb13b79b40 100644 --- a/webapp/templates/jury/delete_modal.html.twig +++ b/webapp/templates/jury/delete_modal.html.twig @@ -1,6 +1,12 @@ {% extends "partials/modal.html.twig" %} -{% block title %}Delete {{ type }} {{ primaryKey }} - "{{ description }}"{% endblock %} +{% block title %} + {% if count > 1 %} + Delete {{ count }} {{ type }}s + {% else %} + Delete {{ type }} {{ primaryKey }} - "{{ description }}" + {% endif %} +{% endblock %} {% block content %} @@ -9,7 +15,16 @@ Error: {{ messages.0 }} {% else %} -
You're about to delete {{ type }} {{ primaryKey }} "{{ description }}".
+ {% if count > 1 %} +You're about to delete the following {{ count }} {{ type }}s:
+You're about to delete {{ type }} {{ primaryKey }} "{{ description }}".
+ {% endif %} {% if messages is not empty %}diff --git a/webapp/templates/jury/jury_macros.twig b/webapp/templates/jury/jury_macros.twig index 8ee16e64d6..9df0ba8c2e 100644 --- a/webapp/templates/jury/jury_macros.twig +++ b/webapp/templates/jury/jury_macros.twig @@ -104,7 +104,7 @@
+
+ {% if problems_current is not empty or problems_other is not empty %} + + {% endif %} {{ button(path('jury_problem_add'), 'Add new problem', 'primary', 'plus') }} {{ button(path('jury_import_export', {'_fragment':'problemarchive'}), 'Import problem', 'primary', 'upload') }}
{% endif %} + + +{% endblock %} + +{% block extrafooter %} + {{ parent() }} + {% endblock %}