From 41dde136e959e94e6c51e292308a5de8bf157a52 Mon Sep 17 00:00:00 2001 From: vincent Date: Fri, 2 Aug 2024 14:54:48 +0200 Subject: [PATCH] essaie de trouver les changesets --- migrations/Version20240802085751.php | 54 +++++++++++ migrations/Version20240802090033.php | 57 +++++++++++ migrations/Version20240802120611.php | 44 +++++++++ src/Controller/TaskController.php | 66 +++++++------ src/Entity/Project.php | 2 +- src/Entity/Task.php | 62 +++++++++++- src/EventSubscriber/TaskLifecycleSubscriber.php | 120 ++++++++++++++++++++++++ src/Security/OpenStreetMapAuthenticator.php | 16 ++-- src/Service/OpenStreetMapClient.php | 84 +++++++++++++++++ src/Service/OsmoseClient.php | 29 ++++++ templates/partials/_task-locking.html.twig | 2 + 11 files changed, 495 insertions(+), 41 deletions(-) create mode 100644 migrations/Version20240802085751.php create mode 100644 migrations/Version20240802090033.php create mode 100644 migrations/Version20240802120611.php create mode 100644 src/EventSubscriber/TaskLifecycleSubscriber.php create mode 100644 src/Service/OpenStreetMapClient.php create mode 100644 src/Service/OsmoseClient.php diff --git a/migrations/Version20240802085751.php b/migrations/Version20240802085751.php new file mode 100644 index 0000000..7bdfbcf --- /dev/null +++ b/migrations/Version20240802085751.php @@ -0,0 +1,54 @@ +addSql('CREATE TEMPORARY TABLE __temp__task AS SELECT id, project_id, created_by_id, locked_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at FROM task'); + $this->addSql('DROP TABLE task'); + $this->addSql('CREATE TABLE task (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, project_id INTEGER NOT NULL, created_by_id INTEGER NOT NULL, locked_by_id INTEGER DEFAULT NULL, done_by_id INTEGER DEFAULT NULL, name VARCHAR(255) NOT NULL, slug VARCHAR(255) NOT NULL, urgent INTEGER DEFAULT NULL, important INTEGER DEFAULT NULL, status VARCHAR(255) NOT NULL, geojson CLOB NOT NULL, osm CLOB DEFAULT NULL, description CLOB DEFAULT NULL, created_at DATETIME NOT NULL --(DC2Type:datetime_immutable) + , locked_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , doing_start_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , doing_end_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , CONSTRAINT FK_527EDB25166D1F9C FOREIGN KEY (project_id) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_527EDB25B03A8386 FOREIGN KEY (created_by_id) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_527EDB257A88E00 FOREIGN KEY (locked_by_id) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_527EDB2535AE3EF9 FOREIGN KEY (done_by_id) REFERENCES user (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO task (id, project_id, created_by_id, locked_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at) SELECT id, project_id, created_by_id, locked_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at FROM __temp__task'); + $this->addSql('DROP TABLE __temp__task'); + $this->addSql('CREATE INDEX IDX_527EDB257A88E00 ON task (locked_by_id)'); + $this->addSql('CREATE INDEX IDX_527EDB25B03A8386 ON task (created_by_id)'); + $this->addSql('CREATE INDEX IDX_527EDB25166D1F9C ON task (project_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_527EDB25989D9B62 ON task (slug)'); + $this->addSql('CREATE INDEX IDX_527EDB2535AE3EF9 ON task (done_by_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TEMPORARY TABLE __temp__task AS SELECT id, project_id, created_by_id, locked_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at FROM task'); + $this->addSql('DROP TABLE task'); + $this->addSql('CREATE TABLE task (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, project_id INTEGER NOT NULL, created_by_id INTEGER NOT NULL, locked_by_id INTEGER DEFAULT NULL, name VARCHAR(255) NOT NULL, slug VARCHAR(255) NOT NULL, urgent INTEGER DEFAULT NULL, important INTEGER DEFAULT NULL, status VARCHAR(255) NOT NULL, geojson CLOB NOT NULL, osm CLOB DEFAULT NULL, description CLOB DEFAULT NULL, created_at DATETIME NOT NULL --(DC2Type:datetime_immutable) + , locked_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , CONSTRAINT FK_527EDB25166D1F9C FOREIGN KEY (project_id) REFERENCES project (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_527EDB25B03A8386 FOREIGN KEY (created_by_id) REFERENCES user (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_527EDB257A88E00 FOREIGN KEY (locked_by_id) REFERENCES user (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO task (id, project_id, created_by_id, locked_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at) SELECT id, project_id, created_by_id, locked_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at FROM __temp__task'); + $this->addSql('DROP TABLE __temp__task'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_527EDB25989D9B62 ON task (slug)'); + $this->addSql('CREATE INDEX IDX_527EDB25166D1F9C ON task (project_id)'); + $this->addSql('CREATE INDEX IDX_527EDB25B03A8386 ON task (created_by_id)'); + $this->addSql('CREATE INDEX IDX_527EDB257A88E00 ON task (locked_by_id)'); + } +} diff --git a/migrations/Version20240802090033.php b/migrations/Version20240802090033.php new file mode 100644 index 0000000..6999146 --- /dev/null +++ b/migrations/Version20240802090033.php @@ -0,0 +1,57 @@ +addSql('CREATE TEMPORARY TABLE __temp__task AS SELECT id, project_id, created_by_id, locked_by_id, done_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at FROM task'); + $this->addSql('DROP TABLE task'); + $this->addSql('CREATE TABLE task (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, project_id INTEGER NOT NULL, created_by_id INTEGER NOT NULL, locked_by_id INTEGER DEFAULT NULL, done_by_id INTEGER DEFAULT NULL, name VARCHAR(255) NOT NULL, slug VARCHAR(255) NOT NULL, urgent INTEGER DEFAULT NULL, important INTEGER DEFAULT NULL, status VARCHAR(255) NOT NULL, geojson CLOB NOT NULL, osm CLOB DEFAULT NULL, description CLOB DEFAULT NULL, created_at DATETIME NOT NULL --(DC2Type:datetime_immutable) + , locked_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , start_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , finish_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , CONSTRAINT FK_527EDB25166D1F9C FOREIGN KEY (project_id) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_527EDB25B03A8386 FOREIGN KEY (created_by_id) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_527EDB257A88E00 FOREIGN KEY (locked_by_id) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_527EDB2535AE3EF9 FOREIGN KEY (done_by_id) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO task (id, project_id, created_by_id, locked_by_id, done_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at) SELECT id, project_id, created_by_id, locked_by_id, done_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at FROM __temp__task'); + $this->addSql('DROP TABLE __temp__task'); + $this->addSql('CREATE INDEX IDX_527EDB2535AE3EF9 ON task (done_by_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_527EDB25989D9B62 ON task (slug)'); + $this->addSql('CREATE INDEX IDX_527EDB25166D1F9C ON task (project_id)'); + $this->addSql('CREATE INDEX IDX_527EDB25B03A8386 ON task (created_by_id)'); + $this->addSql('CREATE INDEX IDX_527EDB257A88E00 ON task (locked_by_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TEMPORARY TABLE __temp__task AS SELECT id, project_id, created_by_id, locked_by_id, done_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at FROM task'); + $this->addSql('DROP TABLE task'); + $this->addSql('CREATE TABLE task (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, project_id INTEGER NOT NULL, created_by_id INTEGER NOT NULL, locked_by_id INTEGER DEFAULT NULL, done_by_id INTEGER DEFAULT NULL, name VARCHAR(255) NOT NULL, slug VARCHAR(255) NOT NULL, urgent INTEGER DEFAULT NULL, important INTEGER DEFAULT NULL, status VARCHAR(255) NOT NULL, geojson CLOB NOT NULL, osm CLOB DEFAULT NULL, description CLOB DEFAULT NULL, created_at DATETIME NOT NULL --(DC2Type:datetime_immutable) + , locked_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , doing_start_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , doing_end_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , CONSTRAINT FK_527EDB25166D1F9C FOREIGN KEY (project_id) REFERENCES project (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_527EDB25B03A8386 FOREIGN KEY (created_by_id) REFERENCES user (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_527EDB257A88E00 FOREIGN KEY (locked_by_id) REFERENCES user (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_527EDB2535AE3EF9 FOREIGN KEY (done_by_id) REFERENCES user (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO task (id, project_id, created_by_id, locked_by_id, done_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at) SELECT id, project_id, created_by_id, locked_by_id, done_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at FROM __temp__task'); + $this->addSql('DROP TABLE __temp__task'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_527EDB25989D9B62 ON task (slug)'); + $this->addSql('CREATE INDEX IDX_527EDB25166D1F9C ON task (project_id)'); + $this->addSql('CREATE INDEX IDX_527EDB25B03A8386 ON task (created_by_id)'); + $this->addSql('CREATE INDEX IDX_527EDB257A88E00 ON task (locked_by_id)'); + $this->addSql('CREATE INDEX IDX_527EDB2535AE3EF9 ON task (done_by_id)'); + } +} diff --git a/migrations/Version20240802120611.php b/migrations/Version20240802120611.php new file mode 100644 index 0000000..0fe8c2f --- /dev/null +++ b/migrations/Version20240802120611.php @@ -0,0 +1,44 @@ +addSql('ALTER TABLE task ADD COLUMN changesets_result CLOB DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TEMPORARY TABLE __temp__task AS SELECT id, project_id, created_by_id, locked_by_id, done_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at, start_at, finish_at FROM task'); + $this->addSql('DROP TABLE task'); + $this->addSql('CREATE TABLE task (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, project_id INTEGER NOT NULL, created_by_id INTEGER NOT NULL, locked_by_id INTEGER DEFAULT NULL, done_by_id INTEGER DEFAULT NULL, name VARCHAR(255) NOT NULL, slug VARCHAR(255) NOT NULL, urgent INTEGER DEFAULT NULL, important INTEGER DEFAULT NULL, status VARCHAR(255) NOT NULL, geojson CLOB NOT NULL, osm CLOB DEFAULT NULL, description CLOB DEFAULT NULL, created_at DATETIME NOT NULL --(DC2Type:datetime_immutable) + , locked_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , start_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , finish_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable) + , CONSTRAINT FK_527EDB25166D1F9C FOREIGN KEY (project_id) REFERENCES project (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_527EDB25B03A8386 FOREIGN KEY (created_by_id) REFERENCES user (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_527EDB257A88E00 FOREIGN KEY (locked_by_id) REFERENCES user (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_527EDB2535AE3EF9 FOREIGN KEY (done_by_id) REFERENCES user (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO task (id, project_id, created_by_id, locked_by_id, done_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at, start_at, finish_at) SELECT id, project_id, created_by_id, locked_by_id, done_by_id, name, slug, urgent, important, status, geojson, osm, description, created_at, locked_at, start_at, finish_at FROM __temp__task'); + $this->addSql('DROP TABLE __temp__task'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_527EDB25989D9B62 ON task (slug)'); + $this->addSql('CREATE INDEX IDX_527EDB25166D1F9C ON task (project_id)'); + $this->addSql('CREATE INDEX IDX_527EDB25B03A8386 ON task (created_by_id)'); + $this->addSql('CREATE INDEX IDX_527EDB257A88E00 ON task (locked_by_id)'); + $this->addSql('CREATE INDEX IDX_527EDB2535AE3EF9 ON task (done_by_id)'); + } +} diff --git a/src/Controller/TaskController.php b/src/Controller/TaskController.php index 5ceca75..31d8f11 100644 --- a/src/Controller/TaskController.php +++ b/src/Controller/TaskController.php @@ -8,6 +8,7 @@ use App\Entity\Task; use App\Form\CommentType; use App\Form\TaskType; use App\Service\GeoJsonManager; +use App\Service\OpenStreetMapClient; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\Extension\Core\Type\SubmitType; @@ -16,8 +17,8 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; -use Symfony\Component\Workflow\WorkflowInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Workflow\WorkflowInterface; #[Route('/task')] class TaskController extends AbstractController @@ -257,7 +258,7 @@ class TaskController extends AbstractController return $this->redirectToRoute('app_project_show', ['slug' => $slug]); } - private function transition(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, $slug, $transitionName, $commentTemplate): Response + private function transition(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, $slug, $transitionName): Response { $repository = $entityManager->getRepository(Task::class); $task = $repository->findOneBySlug($slug); @@ -271,35 +272,10 @@ class TaskController extends AbstractController } try { - // TODO on doit pouvoir faire mieux pour le verrouillage, notamment au niveau des règles de tansition du workflow… - $transitions = array_filter( - $taskLifecycleStateMachine->getDefinition()->getTransitions(), - function ($transition) use ($transitionName) { - return $transitionName === $transition->getName(); - } - ); - $transition = reset($transitions); - $shouldTransitionLock = $taskLifecycleStateMachine->getMetadataStore()->getTransitionMetadata($transition)['lock']; - $shouldTransitionUnlock = $taskLifecycleStateMachine->getMetadataStore()->getTransitionMetadata($transition)['unlock']; - $taskLifecycleStateMachine->apply($task, $transitionName); - if ($shouldTransitionLock) { - $task->lock($this->getUser()); - } elseif ($shouldTransitionUnlock) { - $task->unlock(); - } $entityManager->persist($task); - $comment = new Comment(); - $comment->setTask($task); - $comment->setCreatedBy($this->getUser()); - $comment->setContent($this->renderView($commentTemplate, [ - 'user' => $this->getUser(), - 'task' => $task, - ])); - $entityManager->persist($comment); - $entityManager->flush(); } catch (Exception $exception) { $this->addFlash( @@ -314,25 +290,25 @@ class TaskController extends AbstractController #[Route('/{slug}/start', name: 'app_task_start')] public function start(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, $slug): Response { - return $this->transition($taskLifecycleStateMachine, $entityManager, $slug, Task::TRANSITION_START, 'comment/start.md.twig'); + return $this->transition($taskLifecycleStateMachine, $entityManager, $slug, Task::TRANSITION_START); } #[Route('/{slug}/finish', name: 'app_task_finish')] public function finish(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, $slug): Response { - return $this->transition($taskLifecycleStateMachine, $entityManager, $slug, Task::TRANSITION_FINISH, 'comment/finish.md.twig'); + return $this->transition($taskLifecycleStateMachine, $entityManager, $slug, Task::TRANSITION_FINISH); } #[Route('/{slug}/cancel', name: 'app_task_cancel')] public function cancel(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, $slug): Response { - return $this->transition($taskLifecycleStateMachine, $entityManager, $slug, Task::TRANSITION_CANCEL, 'comment/cancel.md.twig'); + return $this->transition($taskLifecycleStateMachine, $entityManager, $slug, Task::TRANSITION_CANCEL); } #[Route('/{slug}/reset', name: 'app_task_reset')] public function reset(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, $slug): Response { - return $this->transition($taskLifecycleStateMachine, $entityManager, $slug, Task::TRANSITION_RESET, 'comment/reset.md.twig'); + return $this->transition($taskLifecycleStateMachine, $entityManager, $slug, Task::TRANSITION_RESET); } #[Route('/download/{slug}.geojson', name: 'app_task_geojson')] @@ -418,5 +394,33 @@ class TaskController extends AbstractController return $response; } + + #[Route('/{slug}/changesets', name: 'app_task_changesets')] + public function changesets(OpenStreetMapClient $osmClient, EntityManagerInterface $entityManager, $slug): Response + { + $repository = $entityManager->getRepository(Task::class); + $task = $repository->findOneBySlug($slug); + + if (!$task) { + $this->addFlash('warning', 'Tâche non trouvée !'); + return $this->redirect($request->headers->get('referer')); + } + + $task->setChangesetsResult( + json_encode( + $osmClient->getChangesets( + $task->getDoneBy()->getUsername(), + $task->getStartAt(), + $task->getFinishAt() + ) + ) + ); + + $entityManager->persist($task); + $entityManager->flush(); + + return $this->redirectToRoute('app_task_show', ['slug' => $task->getSlug()]); + } + } diff --git a/src/Entity/Project.php b/src/Entity/Project.php index 3735f72..a93b833 100644 --- a/src/Entity/Project.php +++ b/src/Entity/Project.php @@ -269,7 +269,7 @@ class Project $generatedAt = new \DateTimeImmutable($data['osm3s']['timestamp_osm_base']); $now = new \DateTimeImmutable('now'); - $minimum = new \DateInterval('PT1H'); + $minimum = new \DateInterval('PT5M'); $isOutdated = ($generatedAt->add($minimum) < $now); diff --git a/src/Entity/Task.php b/src/Entity/Task.php index a636500..b098366 100644 --- a/src/Entity/Task.php +++ b/src/Entity/Task.php @@ -75,6 +75,18 @@ class Task #[ORM\Column(nullable: true)] private ?\DateTimeImmutable $lockedAt = null; + #[ORM\Column(nullable: true)] + private ?\DateTimeImmutable $startAt = null; + + #[ORM\Column(nullable: true)] + private ?\DateTimeImmutable $finishAt = null; + + #[ORM\ManyToOne] + private ?User $doneBy = null; + + #[ORM\Column(type: Types::TEXT, nullable: true)] + private ?string $changesets_result = null; + public function __construct() { $this->comments = new ArrayCollection(); @@ -266,7 +278,7 @@ class Task public function isLocked(): bool { - return is_null($this->lockedBy); + return !is_null($this->lockedBy); } public function lock(User $user): static @@ -285,4 +297,52 @@ class Task return $this; } + public function getStartAt(): ?\DateTimeImmutable + { + return $this->startAt; + } + + public function setStartAt(?\DateTimeImmutable $startAt): static + { + $this->startAt = $startAt; + + return $this; + } + + public function getFinishAt(): ?\DateTimeImmutable + { + return $this->finishAt; + } + + public function setFinishAt(?\DateTimeImmutable $finishAt): static + { + $this->finishAt = $finishAt; + + return $this; + } + + public function getDoneBy(): ?User + { + return $this->doneBy; + } + + public function setDoneBy(?User $doneBy): static + { + $this->doneBy = $doneBy; + + return $this; + } + + public function getChangesetsResult(): ?string + { + return $this->changesets_result; + } + + public function setChangesetsResult(?string $changesets_result): static + { + $this->changesets_result = $changesets_result; + + return $this; + } + } diff --git a/src/EventSubscriber/TaskLifecycleSubscriber.php b/src/EventSubscriber/TaskLifecycleSubscriber.php new file mode 100644 index 0000000..75e7a22 --- /dev/null +++ b/src/EventSubscriber/TaskLifecycleSubscriber.php @@ -0,0 +1,120 @@ +getTransition(); + $task = $event->getSubject(); + + // Gère le verrouillage de la tâche + + $shouldLock = $this->taskLifecycleStateMachine->getMetadataStore()->getTransitionMetadata($transition)['lock']; + $shouldUnlock = $this->taskLifecycleStateMachine->getMetadataStore()->getTransitionMetadata($transition)['unlock']; + + if ($shouldLock) { + $task->lock($this->security->getUser()); + } elseif ($shouldUnlock) { + $task->unlock(); + } + + // Commentaire automatique + $user = $this->security->getUser(); + $task = $event->getSubject(); + $comment = new Comment(); + $comment->setTask($task); + $comment->setCreatedBy($user); + $comment->setContent($this->twig->render(sprintf('comment/%s.md.twig', $transition->getName()), [ + 'user' => $user, + 'task' => $task, + ])); + $this->entityManager->persist($comment); + $this->entityManager->flush(); + } + + public function onGuard(Event $event): void + { + $transition = $event->getTransition(); + $task = $event->getSubject(); + + $shouldLock = $this->taskLifecycleStateMachine->getMetadataStore()->getTransitionMetadata($transition)['lock']; + $shouldUnlock = $this->taskLifecycleStateMachine->getMetadataStore()->getTransitionMetadata($transition)['unlock']; + + $subject = $event->getSubject(); + + if ($shouldLock and $task->isLocked()) { + $event->setBlocked(true, 'La tâche est déjà verrouillée.'); + } elseif ($shouldUnlock and !$task->isLocked()) { + $event->setBlocked(true, 'La tâche n’est pas déjà verrouillée.'); + } + + $event->setBlocked(false); + } + + public function onStart(Event $event): void + { + $task = $event->getSubject(); + + $task->setStartAt(new \DateTimeImmutable('now')); + } + + public function onFinish(Event $event): void + { + $task = $event->getSubject(); + + $task->setFinishAt(new \DateTimeImmutable('now')); + } + + public function onDone(Event $event): void + { + $task = $event->getSubject(); + $user = $this->security->getUser(); + + $task->setDoneBy($user); + + $task->setChangesetsResult( + json_encode($this->osmClient()->getChangesets( + $user->getUsername(), + $task->getStartAt(), + $task->getFinishAt() + )) + ); + } + + public static function getSubscribedEvents(): array + { + return [ + TransitionEvent::getName('task_lifecycle', null) => 'onTransition', + GuardEvent::getName('task_lifecycle', null) => 'onGuard', + TransitionEvent::getName('task_lifecycle', Task::TRANSITION_START) => 'onStart', + TransitionEvent::getName('task_lifecycle', Task::TRANSITION_FINISH) => 'onFinish', + EnteredEvent::getName('task_lifecycle', Task::STATUS_DONE) => 'onDone', + ]; + } + +} diff --git a/src/Security/OpenStreetMapAuthenticator.php b/src/Security/OpenStreetMapAuthenticator.php index b40011a..eb4c50b 100644 --- a/src/Security/OpenStreetMapAuthenticator.php +++ b/src/Security/OpenStreetMapAuthenticator.php @@ -19,15 +19,12 @@ use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface class OpenStreetMapAuthenticator extends OAuth2Authenticator implements AuthenticationEntrypointInterface { - private $clientRegistry; - private $entityManager; - private $router; - - public function __construct(ClientRegistry $clientRegistry, EntityManagerInterface $entityManager, RouterInterface $router) + public function __construct( + private ClientRegistry $clientRegistry, + private EntityManagerInterface $entityManager, + private RouterInterface $router, + ) { - $this->clientRegistry = $clientRegistry; - $this->entityManager = $entityManager; - $this->router = $router; } public function supports(Request $request): ?bool @@ -40,6 +37,9 @@ class OpenStreetMapAuthenticator extends OAuth2Authenticator implements Authenti $client = $this->clientRegistry->getClient('openstreetmap'); $accessToken = $this->fetchAccessToken($client); + $session = $request->getSession(); + $session->set('access_token', $accessToken); + return new SelfValidatingPassport( new UserBadge($accessToken->getToken(), function() use ($accessToken, $client) { $resourceOwner = $client->fetchUserFromToken($accessToken); diff --git a/src/Service/OpenStreetMapClient.php b/src/Service/OpenStreetMapClient.php new file mode 100644 index 0000000..626d2a8 --- /dev/null +++ b/src/Service/OpenStreetMapClient.php @@ -0,0 +1,84 @@ +requestStack->getSession(); + $accessToken = $session->get('access_token'); + /* + if ($accessToken->hasExpired()) { + $accessToken = $this->clientRegistry->getClient('openstreetmap')->refreshAccessToken($accessToken->getRefreshToken()); + $session->set('access_token', $accessToken); + } + */ + $token = $accessToken->getToken(); + + $response = $this->client->request($method, 'https://api.openstreetmap.org/api/0.6/' . $path, [ + 'auth_bearer' => $token, + 'query' => $params, + ]); + + $isStatusCodeOk = ($response->getStatusCode() === 200); + + if (!$isStatusCodeOk) { + throw new \RuntimeException(); + } + + return $response->getContent(); + } + + /* Cf */ + public function getChangesets(?string $username = null, ?\DateTimeInterface $since = null, ?\DateTimeInterface $then = null) + { + $changesets = []; + + $params = []; + + if (!is_null($username)) { + $params['display_name'] = $username; + } + + if (!is_null($since) and !is_null($then)) { + $params['time'] = implode(',', [ + $since->format(\DateTimeInterface::ISO8601), + $then->format(\DateTimeInterface::ISO8601), + ]); + } elseif (!is_null($since) and is_null($then)) { + $params['time'] = $since->format(\DateTimeInterface::ISO8601); + } + + $response = $this->query('GET', 'changesets', $params); + $xml = new \DOMDocument(); + $xml->loadXML($response); + $xpath = new \DOMXPath($xml); + $changesetNodes = $xpath->query('/osm/changeset'); + foreach ($changesetNodes as $changesetNode) { + $changeset = []; + foreach ($changesetNode->attributes as $attribute) { + $changeset[$attribute->name] = $attribute->value; + } + $tagNodes = $xpath->query('./tag', $changesetNode); + foreach ($tagNodes as $tagNode) { + $changeset[$tagNode->getAttribute('k')] = $tagNode->getAttribute('v'); + } + $changesets[] = $changeset; + } + + return $changesets; + } + +} diff --git a/src/Service/OsmoseClient.php b/src/Service/OsmoseClient.php new file mode 100644 index 0000000..237068c --- /dev/null +++ b/src/Service/OsmoseClient.php @@ -0,0 +1,29 @@ + @@ -18,3 +19,4 @@ {% endif %} +{% endif %}