Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions webapp/public/js/multi-delete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
function initializeMultiDelete(options) {
var $deleteButton = $(options.buttonSelector);
var checkboxClass = options.checkboxClass;
var deleteUrl = options.deleteUrl;

function toggleDeleteButton() {
var checkedCount = $('.' + checkboxClass + ':checked').length;
$deleteButton.prop('disabled', checkedCount === 0);
}

$(document).on('change', '.' + checkboxClass, function() {
var table = $(this).closest('table');
var $tableCheckboxes = table.find('.' + checkboxClass);
var $selectAllInTable = table.find('.select-all');
$selectAllInTable.prop('checked', $tableCheckboxes.length > 0 && $tableCheckboxes.length === $tableCheckboxes.filter(':checked').length);
toggleDeleteButton();
});

$(document).on('change', '.select-all', function() {
var table = $(this).closest('table');
table.find('.' + checkboxClass).prop('checked', $(this).is(':checked'));
toggleDeleteButton();
});

toggleDeleteButton();

$deleteButton.on('click', function () {
var ids = $('.' + checkboxClass + ':checked').map(function () {
return 'ids[]=' + $(this).val();
}).get();
if (ids.length === 0) return;

var url = deleteUrl + '?' + ids.join('&');

var $tempLink = $('<a>', {
'href': url,
'data-ajax-modal': ''
}).hide().appendTo('body');

$tempLink.trigger('click');
$tempLink.remove();
});
}
32 changes: 32 additions & 0 deletions webapp/src/Controller/Jury/TeamAffiliationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\PropertyAccess\PropertyAccess;
Expand Down Expand Up @@ -59,6 +60,13 @@ public function indexAction(
'name' => ['title' => 'name', 'sort' => true, 'default_sort' => true],
];

if ($this->isGranted('ROLE_ADMIN')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we maybe make a generalized method of this in the base class? Also for the one below? So that we don't have this repeated html everywhere, if we ever want to change styling or something.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, please check the last two commits.

$table_fields = array_merge(
['checkbox' => ['title' => '<input type="checkbox" class="select-all" title="Select all affiliations">', 'sort' => false, 'search' => false, 'raw' => true]],
$table_fields
);
}

if ($showFlags) {
$table_fields['country'] = ['title' => 'country', 'sort' => true];
$table_fields['affiliation_logo'] = ['title' => 'logo', 'sort' => false];
Expand All @@ -73,6 +81,16 @@ public function indexAction(
$teamAffiliation = $teamAffiliationData[0];
$affiliationdata = [];
$affiliationactions = [];

if ($this->isGranted('ROLE_ADMIN')) {
$affiliationdata['checkbox'] = [
'value' => sprintf(
'<input type="checkbox" name="ids[]" value="%s" class="affiliation-checkbox">',
$teamAffiliation->getAffilid()
)
];
}

// Get whatever fields we can from the affiliation object itself.
foreach ($table_fields as $k => $v) {
if ($propertyAccessor->isReadable($teamAffiliation, $k)) {
Expand Down Expand Up @@ -201,6 +219,20 @@ public function deleteAction(Request $request, int $affilId): Response
return $this->deleteEntities($request, [$teamAffiliation], $this->generateUrl('jury_team_affiliations'));
}

#[IsGranted('ROLE_ADMIN')]
#[Route(path: '/delete-multiple', name: 'jury_team_affiliation_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');
}

$affiliations = $this->em->getRepository(TeamAffiliation::class)->findBy(['affilid' => $ids]);

return $this->deleteEntities($request, $affiliations, $this->generateUrl('jury_team_affiliations'));
}

#[IsGranted('ROLE_ADMIN')]
#[Route(path: '/add', name: 'jury_team_affiliation_add')]
public function addAction(Request $request): Response
Expand Down
32 changes: 32 additions & 0 deletions webapp/src/Controller/Jury/TeamCategoryController.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\PropertyAccess\PropertyAccess;
Expand Down Expand Up @@ -65,13 +66,30 @@ public function indexAction(): Response
'allow_self_registration' => ['title' => 'self-registration', 'sort' => true],
];

if ($this->isGranted('ROLE_ADMIN')) {
$table_fields = array_merge(
['checkbox' => ['title' => '<input type="checkbox" class="select-all" title="Select all categories">', 'sort' => false, 'search' => false, 'raw' => true]],
$table_fields
);
}

$propertyAccessor = PropertyAccess::createPropertyAccessor();
$team_categories_table = [];
foreach ($teamCategories as $teamCategoryData) {
/** @var TeamCategory $teamCategory */
$teamCategory = $teamCategoryData[0];
$categorydata = [];
$categoryactions = [];

if ($this->isGranted('ROLE_ADMIN')) {
$categorydata['checkbox'] = [
'value' => sprintf(
'<input type="checkbox" name="ids[]" value="%s" class="category-checkbox">',
$teamCategory->getCategoryid()
)
];
}

// Get whatever fields we can from the category object itself.
foreach ($table_fields as $k => $v) {
if ($propertyAccessor->isReadable($teamCategory, $k)) {
Expand Down Expand Up @@ -230,6 +248,20 @@ public function addAction(Request $request): Response
]);
}

#[IsGranted('ROLE_ADMIN')]
#[Route(path: '/delete-multiple', name: 'jury_team_category_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');
}

$categories = $this->em->getRepository(TeamCategory::class)->findBy(['categoryid' => $ids]);

return $this->deleteEntities($request, $categories, $this->generateUrl('jury_team_categories'));
}

#[Route(path: '/{categoryId<\d+>}/request-remaining', name: 'jury_team_category_request_remaining')]
public function requestRemainingRunsWholeTeamCategoryAction(string $categoryId): RedirectResponse
{
Expand Down
78 changes: 78 additions & 0 deletions webapp/src/Controller/Jury/TeamController.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ public function indexAction(): Response
'stats' => ['title' => 'stats', 'sort' => true,],
];

if ($this->isGranted('ROLE_ADMIN')) {
$table_fields = array_merge(
['checkbox' => ['title' => '<input type="checkbox" class="select-all" title="Select all teams">', 'sort' => false, 'search' => false, 'raw' => true]],
$table_fields
);
}

$userDataPerTeam = $this->em->createQueryBuilder()
->from(Team::class, 't', 't.teamid')
->leftJoin('t.users', 'u')
Expand All @@ -115,6 +122,36 @@ public function indexAction(): Response
foreach ($teams as $t) {
$teamdata = [];
$teamactions = [];

if ($this->isGranted('ROLE_ADMIN')) {
$isLocked = false;
foreach ($t->getContests() as $contest) {
if ($contest->isLocked()) {
$isLocked = true;
break;
}
}
if (!$isLocked && $t->getCategory()) {
foreach ($t->getCategory()->getContests() as $contest) {
if ($contest->isLocked()) {
$isLocked = true;
break;
}
}
}

if (!$isLocked) {
$teamdata['checkbox'] = [
'value' => sprintf(
'<input type="checkbox" name="ids[]" value="%s" class="team-checkbox">',
$t->getTeamid()
)
];
} else {
$teamdata['checkbox'] = ['value' => ''];
}
}

// Get whatever fields we can from the team object itself.
foreach ($table_fields as $k => $v) {
if ($propertyAccessor->isReadable($t, $k)) {
Expand Down Expand Up @@ -346,6 +383,47 @@ public function deleteAction(Request $request, int $teamId): Response
return $this->deleteEntities($request, [$team], $this->generateUrl('jury_teams'));
}

#[IsGranted('ROLE_ADMIN')]
#[Route(path: '/delete-multiple', name: 'jury_team_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');
}

$teams = $this->em->getRepository(Team::class)->findBy(['teamid' => $ids]);

$deletableTeams = [];
foreach ($teams as $team) {
$isLocked = false;
foreach ($team->getContests() as $contest) {
if ($contest->isLocked()) {
$isLocked = true;
break;
}
}
if (!$isLocked && $team->getCategory()) {
foreach ($team->getCategory()->getContests() as $contest) {
if ($contest->isLocked()) {
$isLocked = true;
break;
}
}
}
if (!$isLocked) {
$deletableTeams[] = $team;
}
}

if (empty($deletableTeams)) {
$this->addFlash('warning', 'No teams could be deleted (they might be in a locked contest).');
return $this->redirectToRoute('jury_teams');
}

return $this->deleteEntities($request, $deletableTeams, $this->generateUrl('jury_teams'));
}

#[IsGranted('ROLE_ADMIN')]
#[Route(path: '/add', name: 'jury_team_add')]
public function addAction(Request $request): Response
Expand Down
50 changes: 50 additions & 0 deletions webapp/src/Controller/Jury/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\PropertyAccess\PropertyAccess;
Expand Down Expand Up @@ -70,6 +71,14 @@ public function indexAction(): Response
'teamid' => ['title' => '', 'sort' => false, 'render' => 'entity_id_badge'],
'team' => ['title' => 'team', 'sort' => true],
];

if ($this->isGranted('ROLE_ADMIN')) {
$table_fields = array_merge(
['checkbox' => ['title' => '<input type="checkbox" class="select-all" title="Select all users">', 'sort' => false, 'search' => false, 'raw' => true]],
$table_fields
);
}

if (in_array('ipaddress', $this->config->get('auth_methods'))) {
$table_fields['ip_address'] = ['title' => 'autologin IP', 'sort' => true];
}
Expand All @@ -83,6 +92,21 @@ public function indexAction(): Response
/** @var User $u */
$userdata = [];
$useractions = [];

if ($this->isGranted('ROLE_ADMIN')) {
$canBeDeleted = $u->getUserid() !== $this->dj->getUser()->getUserid();
if ($canBeDeleted) {
$userdata['checkbox'] = [
'value' => sprintf(
'<input type="checkbox" name="ids[]" value="%s" class="user-checkbox">',
$u->getUserid()
)
];
} else {
$userdata['checkbox'] = ['value' => ''];
}
}

// Get whatever fields we can from the user object itself.
foreach ($table_fields as $k => $v) {
if ($propertyAccessor->isReadable($u, $k)) {
Expand Down Expand Up @@ -386,4 +410,30 @@ public function resetTeamLoginStatus(Request $request): Response
$this->addFlash('success', 'Reset login status all ' . $count . ' users with the team role.');
return $this->redirectToRoute('jury_users');
}

#[IsGranted('ROLE_ADMIN')]
#[Route(path: '/delete-multiple', name: 'jury_user_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');
}

$users = $this->em->getRepository(User::class)->findBy(['userid' => $ids]);

$deletableUsers = [];
foreach ($users as $user) {
if ($user->getUserid() !== $this->dj->getUser()->getUserid()) {
$deletableUsers[] = $user;
}
}

if (empty($deletableUsers)) {
$this->addFlash('warning', 'No users could be deleted (you cannot delete your own account).');
return $this->redirectToRoute('jury_users');
}

return $this->deleteEntities($request, $deletableUsers, $this->generateUrl('jury_users'));
}
}
6 changes: 3 additions & 3 deletions webapp/templates/jury/delete_modal.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

{% block title %}
{% if count > 1 %}
Delete {{ count }} {{ type }}s
Delete {{ count }} {% if type|slice(-1) == 'y' %}{{ type|slice(0, -1) }}ies{% else %}{{ type }}s{% endif %}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eldering this is the fix for pluralizing team category

{% else %}
Delete {{ type }} {{ primaryKey }} - "{{ description }}"
{% endif %}
Expand All @@ -16,7 +16,7 @@
</div>
{% else %}
{% if count > 1 %}
<p>You're about to delete the following {{ count }} {{ type }}s:</p>
<p>You're about to delete the following {{ count }} {% if type|slice(-1) == 'y' %}{{ type|slice(0, -1) }}ies{% else %}{{ type }}s{% endif %}:</p>
<ul>
{% for desc in description|split(',') %}
<li>{{ desc|trim }}</li>
Expand Down Expand Up @@ -46,4 +46,4 @@
{% if isError %}OK{% else %}Cancel{% endif %}
{% endblock %}

{% block buttonText %}Delete{% endblock %}
{% block buttonText %}Delete{% endblock %}
3 changes: 3 additions & 0 deletions webapp/templates/jury/partials/_delete_button.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{% if entities is not empty %}
<button type="button" class="btn btn-danger me-2" id="delete-selected-button" disabled><i class="fas fa-trash-alt"></i> Delete selected</button>
{% endif %}
Loading
Loading