version 1.16.2 (#2942)

* bump version
* include calendar week in week chooser
* table names in SQL
* show save flash message
* prevent migration warning
* drop default value to prevent error when server version is not set
* csrf token for duplicate actions
* updated translations
This commit is contained in:
Kevin Papst
2021-11-18 12:33:13 +01:00
committed by GitHub
parent c858edf5c9
commit b28e9c120c
37 changed files with 289 additions and 152 deletions

View File

@@ -421,13 +421,23 @@ final class ProjectController extends AbstractController
}
/**
* @Route(path="/{id}/duplicate", name="admin_project_duplicate", methods={"GET", "POST"})
* @Route(path="/{id}/duplicate/{token}", name="admin_project_duplicate", methods={"GET", "POST"})
* @Security("is_granted('edit', project)")
*/
public function duplicateAction(Project $project, Request $request, ProjectDuplicationService $projectDuplicationService)
public function duplicateAction(Project $project, string $token, ProjectDuplicationService $projectDuplicationService, CsrfTokenManagerInterface $csrfTokenManager)
{
if (!$csrfTokenManager->isTokenValid(new CsrfToken('project.duplicate', $token))) {
$this->flashError('action.csrf.error');
return $this->redirectToRoute('project_details', ['id' => $project->getId()]);
}
$csrfTokenManager->refreshToken($token);
$newProject = $projectDuplicationService->duplicate($project, $project->getName() . ' [COPY]');
$this->flashSuccess('action.update.success');
return $this->redirectToRoute('project_details', ['id' => $newProject->getId()]);
}

View File

@@ -22,6 +22,8 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
/**
* @Route(path="/admin/teams")
@@ -81,11 +83,19 @@ final class TeamController extends AbstractController
}
/**
* @Route(path="/{id}/duplicate", name="team_duplicate", methods={"GET", "POST"})
* @Route(path="/{id}/duplicate/{token}", name="team_duplicate", methods={"GET", "POST"})
* @Security("is_granted('edit', team) and is_granted('create_team')")
*/
public function duplicateTeam(Team $team, Request $request)
public function duplicateTeam(Team $team, string $token, CsrfTokenManagerInterface $csrfTokenManager)
{
if (!$csrfTokenManager->isTokenValid(new CsrfToken('team.duplicate', $token))) {
$this->flashError('action.csrf.error');
return $this->redirectToRoute('admin_team_edit', ['id' => $team->getId()]);
}
$csrfTokenManager->refreshToken($token);
$newTeam = clone $team;
$newTeam->setName($team->getName() . ' [COPY]');

View File

@@ -211,14 +211,14 @@ abstract class TimesheetAbstractController extends AbstractController
]);
}
protected function duplicate(Timesheet $timesheet, Request $request, string $renderTemplate): Response
protected function duplicate(Timesheet $timesheet, Request $request, string $renderTemplate, string $token): Response
{
$copyTimesheet = clone $timesheet;
$event = new TimesheetMetaDefinitionEvent($copyTimesheet);
$this->dispatcher->dispatch($event);
$form = $this->getDuplicateForm($copyTimesheet, $timesheet);
$form = $this->getDuplicateForm($copyTimesheet, $timesheet, $token);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
@@ -612,7 +612,7 @@ abstract class TimesheetAbstractController extends AbstractController
return $query;
}
abstract protected function getDuplicateForm(Timesheet $entry, Timesheet $original): FormInterface;
abstract protected function getDuplicateForm(Timesheet $entry, Timesheet $original, string $token): FormInterface;
abstract protected function getCreateForm(Timesheet $entry): FormInterface;
}

View File

@@ -21,6 +21,8 @@ use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
/**
* @Route(path="/timesheet")
@@ -60,12 +62,20 @@ class TimesheetController extends TimesheetAbstractController
}
/**
* @Route(path="/{id}/duplicate", name="timesheet_duplicate", methods={"GET", "POST"})
* @Route(path="/{id}/duplicate/{token}", name="timesheet_duplicate", methods={"GET", "POST"})
* @Security("is_granted('duplicate', entry)")
*/
public function duplicateAction(Timesheet $entry, Request $request): Response
public function duplicateAction(Timesheet $entry, Request $request, string $token, CsrfTokenManagerInterface $csrfTokenManager): Response
{
return $this->duplicate($entry, $request, 'timesheet/edit.html.twig');
if (!$csrfTokenManager->isTokenValid(new CsrfToken('timesheet.duplicate', $token))) {
$this->flashError('action.csrf.error');
return $this->redirectToRoute('timesheet');
}
$csrfTokenManager->refreshToken($token);
return $this->duplicate($entry, $request, 'timesheet/edit.html.twig', $token);
}
/**
@@ -100,8 +110,8 @@ class TimesheetController extends TimesheetAbstractController
return $this->generateCreateForm($entry, TimesheetEditForm::class, $this->generateUrl('timesheet_create'));
}
protected function getDuplicateForm(Timesheet $entry, Timesheet $original): FormInterface
protected function getDuplicateForm(Timesheet $entry, Timesheet $original, string $token): FormInterface
{
return $this->generateCreateForm($entry, TimesheetEditForm::class, $this->generateUrl('timesheet_duplicate', ['id' => $original->getId()]));
return $this->generateCreateForm($entry, TimesheetEditForm::class, $this->generateUrl('timesheet_duplicate', ['id' => $original->getId(), 'token' => $token]));
}
}

View File

@@ -28,6 +28,8 @@ use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
/**
* @Route(path="/team/timesheet")
@@ -71,12 +73,20 @@ class TimesheetTeamController extends TimesheetAbstractController
}
/**
* @Route(path="/{id}/duplicate", name="admin_timesheet_duplicate", methods={"GET", "POST"})
* @Route(path="/{id}/duplicate/{token}", name="admin_timesheet_duplicate", methods={"GET", "POST"})
* @Security("is_granted('duplicate', entry)")
*/
public function duplicateAction(Timesheet $entry, Request $request): Response
public function duplicateAction(Timesheet $entry, Request $request, string $token, CsrfTokenManagerInterface $csrfTokenManager): Response
{
return $this->duplicate($entry, $request, 'timesheet-team/edit.html.twig');
if (!$csrfTokenManager->isTokenValid(new CsrfToken('admin_timesheet.duplicate', $token))) {
$this->flashError('action.csrf.error');
return $this->redirectToRoute('admin_timesheet');
}
$csrfTokenManager->refreshToken($token);
return $this->duplicate($entry, $request, 'timesheet-team/edit.html.twig', $token);
}
/**
@@ -195,9 +205,9 @@ class TimesheetTeamController extends TimesheetAbstractController
return $this->generateCreateForm($entry, TimesheetAdminEditForm::class, $this->generateUrl('admin_timesheet_create'));
}
protected function getDuplicateForm(Timesheet $entry, Timesheet $original): FormInterface
protected function getDuplicateForm(Timesheet $entry, Timesheet $original, string $token): FormInterface
{
return $this->generateCreateForm($entry, TimesheetAdminEditForm::class, $this->generateUrl('admin_timesheet_duplicate', ['id' => $original->getId()]));
return $this->generateCreateForm($entry, TimesheetAdminEditForm::class, $this->generateUrl('admin_timesheet_duplicate', ['id' => $original->getId(), 'token' => $token]));
}
protected function getPermissionEditExport(): string