<?php
|
|
|
|
namespace App\Controller;
|
|
|
|
use App\Entity\Comment;
|
|
use App\Entity\Project;
|
|
use App\Entity\Task;
|
|
use App\Form\CommentType;
|
|
use App\Form\TaskType;
|
|
use App\Service\GeoJsonManager;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
|
use Symfony\Component\HttpFoundation\HeaderUtils;
|
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
|
use Symfony\Component\Routing\Attribute\Route;
|
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
|
use Symfony\Component\Workflow\WorkflowInterface;
|
|
|
|
#[Route('/task')]
|
|
class TaskController extends AbstractController
|
|
{
|
|
// Page de créatiom d’une tâche
|
|
#[Route('/create', name: 'app_task_create')]
|
|
public function create(Request $request, EntityManagerInterface $entityManager): Response
|
|
{
|
|
if (!$request->query->has('slug')) {
|
|
$this->addFlash('warning', 'Projet non spécifié !');
|
|
|
|
return $this->redirectToRoute('app_project');
|
|
}
|
|
|
|
$slug = $request->query->get('slug');
|
|
|
|
$repository = $entityManager->getRepository(Project::class);
|
|
$project = $repository->findOneBySlug($slug);
|
|
|
|
if (!$project) {
|
|
$this->addFlash('warning', 'Projet non trouvé !');
|
|
|
|
return $this->redirectToRoute('app_project');
|
|
}
|
|
|
|
$task = new Task();
|
|
$task->setProject($project);
|
|
$createForm = $this->createForm(TaskType::class, $task);
|
|
$createForm->add('submit', SubmitType::class, [
|
|
'label' => 'Créer',
|
|
]);
|
|
|
|
$createForm->handleRequest($request);
|
|
if ($createForm->isSubmitted() and $createForm->isValid()) {
|
|
$task = $createForm->getData();
|
|
|
|
try {
|
|
if (!$task->hasGeojson()) {
|
|
$task->setGeojson('{}');
|
|
}
|
|
$task->setCreatedBy($this->getUser());
|
|
$entityManager->persist($task);
|
|
$entityManager->flush();
|
|
|
|
$this->addFlash('success', 'Tâche créée !');
|
|
|
|
return $this->redirectToRoute('app_project_show', ['slug' => $slug]);
|
|
} catch (\Exception $exception) {
|
|
$this->addFlash('danger', 'Impossible de créer la tâche !');
|
|
}
|
|
}
|
|
|
|
return $this->render('task/create.html.twig', [
|
|
'project' => $project,
|
|
'create_form' => $createForm,
|
|
]);
|
|
}
|
|
|
|
// Page spécifique à une tâche, où l’on trouve tout ce qui la concerne
|
|
#[Route('/{slug}', name: 'app_task_show')]
|
|
public function show(Request $request, EntityManagerInterface $entityManager, GeoJsonManager $geoJsonManager, $slug): Response
|
|
{
|
|
$repository = $entityManager->getRepository(Task::class);
|
|
$task = $repository->findOneBySlug($slug);
|
|
|
|
if (!$task) {
|
|
$this->addFlash('warning', 'Tâche non trouvée !');
|
|
|
|
if (!$request->headers->has('Referer')) {
|
|
throw $this->createNotFoundException('Task not found');
|
|
}
|
|
|
|
return $this->redirect($request->headers->get('Referer'));
|
|
}
|
|
|
|
$project = $task->getProject();
|
|
$randomTask = $repository->findRandomByProject($project, Task::STATUS_TODO);
|
|
|
|
$comment = new Comment();
|
|
$commentForm = $this->createForm(CommentType::class, $comment, [
|
|
'action' => $this->generateUrl('app_task_comment', ['slug' => $slug]),
|
|
]);
|
|
$commentForm->add('submit', SubmitType::class, [
|
|
'label' => 'Commenter',
|
|
]);
|
|
|
|
// Programmation de la télécommande JOSM
|
|
|
|
$josmCommands = [
|
|
// Charge l’imagerie (fond OSM par défaut)
|
|
'imagery' => [
|
|
'id' => $project->hasImagery() ? $project->getImagery() : 'osmfr',
|
|
],
|
|
// Charge le XML OSM du projet danms un calque de données
|
|
// TODO C’est susceptible de planter s’il n’y a pas d’OSM dans le projet mais est-ce possible ?
|
|
'import' => [
|
|
'new_layer' => true,
|
|
'layer_name' => $task->getName(),
|
|
'url' => $this->generateUrl(
|
|
'app_task_osm',
|
|
['slug' => $task->getSlug()],
|
|
UrlGeneratorInterface::ABSOLUTE_URL
|
|
),
|
|
],
|
|
];
|
|
|
|
if ($task->hasGeojson()) {
|
|
$geom = \geoPHP::load($task->getGeojson(), 'json');
|
|
$bbox = $geom->getBBox();
|
|
|
|
// Zoome sur les données et préremplit le changeset
|
|
$josmCommands['zoom'] = [
|
|
'bottom' => $bbox['miny'],
|
|
'top' => $bbox['maxy'],
|
|
'left' => $bbox['minx'],
|
|
'right' => $bbox['maxx'],
|
|
'changeset_comment' => sprintf('%s %s', $project->getName(), $task->getName()),
|
|
'changeset_source' => $project->getSource(),
|
|
'changeset_hashtags' => $project->getHashtags(),
|
|
];
|
|
}
|
|
|
|
return $this->render('task/show.html.twig', [
|
|
'task' => $task,
|
|
'project' => $project,
|
|
'commentForm' => $commentForm,
|
|
'randomTask' => $randomTask,
|
|
'josmCommands' => json_encode($josmCommands),
|
|
]);
|
|
}
|
|
|
|
// Ajoute un commentaire à la tâche
|
|
#[Route('/{slug}/comment', name: 'app_task_comment')]
|
|
public function comment(Request $request, 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'));
|
|
}
|
|
|
|
$comment = new Comment();
|
|
$comment->setCreatedBy($this->getUser());
|
|
$comment->setTask($task);
|
|
$commentForm = $this->createForm(CommentType::class, $comment);
|
|
$commentForm->add('submit', SubmitType::class, [
|
|
'label' => 'Commenter',
|
|
]);
|
|
|
|
$commentForm->handleRequest($request);
|
|
if ($commentForm->isSubmitted() and $commentForm->isValid()) {
|
|
$comment = $commentForm->getData();
|
|
|
|
try {
|
|
$entityManager->persist($comment);
|
|
$entityManager->flush();
|
|
} catch (\Exception $exception) {
|
|
$this->addFlash('danger', 'Impossible de commenter ! '.$exception->getMessage());
|
|
}
|
|
}
|
|
|
|
return $this->redirectToRoute('app_task_show', ['slug' => $slug]);
|
|
}
|
|
|
|
// Modifie les informations d’une tâche
|
|
#[Route('/{slug}/update', name: 'app_task_update')]
|
|
public function update(Request $request, 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'));
|
|
}
|
|
|
|
$updateForm = $this->createForm(TaskType::class, $task);
|
|
$updateForm->add('submit', SubmitType::class, [
|
|
'label' => 'Modifier',
|
|
]);
|
|
|
|
$updateForm->handleRequest($request);
|
|
if ($updateForm->isSubmitted() and $updateForm->isValid()) {
|
|
$task = $updateForm->getData();
|
|
|
|
try {
|
|
$entityManager->persist($task);
|
|
$entityManager->flush();
|
|
|
|
return $this->redirectToRoute('app_task_show', ['slug' => $slug]);
|
|
} catch (\Exception $exception) {
|
|
$this->addFlash('danger', 'Impossible de modifier la tâche !');
|
|
}
|
|
}
|
|
|
|
return $this->render('task/update.html.twig', [
|
|
'project' => $task->getProject(),
|
|
'task' => $task,
|
|
'update_form' => $updateForm,
|
|
]);
|
|
}
|
|
|
|
// Supprimer une tâche
|
|
#[Route('/{slug}/remove', name: 'app_task_remove')]
|
|
public function remove(Request $request, 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'));
|
|
}
|
|
|
|
$project = $task->getProject();
|
|
|
|
try {
|
|
$entityManager->remove($task);
|
|
$entityManager->flush();
|
|
|
|
return $this->redirectToRoute('app_project_show', ['slug' => $project->getSlug()]);
|
|
} catch (\Exception $exception) {
|
|
$this->addFlash('danger', 'Impossible de supprimer la tâche !');
|
|
}
|
|
|
|
return $this->redirectToRoute('app_project_show', ['slug' => $slug]);
|
|
}
|
|
|
|
// Passe une tâche d’un état à un autre
|
|
private function transition(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, $slug, $transitionName): Response
|
|
{
|
|
$repository = $entityManager->getRepository(Task::class);
|
|
$task = $repository->findOneBySlug($slug);
|
|
|
|
if (!$task) {
|
|
$this->addFlash('warning', 'Tâche non trouvée !');
|
|
|
|
return $this->redirectToRoute('app_project');
|
|
}
|
|
|
|
try {
|
|
$taskLifecycleStateMachine->apply($task, $transitionName);
|
|
|
|
$entityManager->persist($task);
|
|
|
|
$entityManager->flush();
|
|
} catch (Exception $exception) {
|
|
$this->addFlash('warning', 'Impossible de modifier la tâche !');
|
|
}
|
|
|
|
return $this->redirectToRoute('app_task_show', ['slug' => $slug]);
|
|
}
|
|
|
|
// Commence une tâche
|
|
#[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);
|
|
}
|
|
|
|
// Termine une tâche
|
|
#[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);
|
|
}
|
|
|
|
// Abandonne une tâche
|
|
#[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);
|
|
}
|
|
|
|
// Recommence une tâche
|
|
#[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);
|
|
}
|
|
|
|
// Renvoie le geojson associé à une tâche
|
|
#[Route('/download/{slug}.geojson', name: 'app_task_geojson')]
|
|
public function geojson(Request $request, EntityManagerInterface $entityManager, $slug): Response
|
|
{
|
|
$repository = $entityManager->getRepository(Task::class);
|
|
$task = $repository->findOneBySlug($slug);
|
|
|
|
if (!$task) {
|
|
$this->addFlash('warning', 'Tâche non trouvée !');
|
|
|
|
if (!$request->headers->has('Referer')) {
|
|
throw $this->createNotFoundException('Task not found');
|
|
}
|
|
|
|
return $this->redirect($request->headers->get('Referer'));
|
|
}
|
|
|
|
if (!$task->hasGeojson()) {
|
|
return new Response('Not found', 404);
|
|
}
|
|
|
|
$response = JsonResponse::fromJsonString($task->getGeojson());
|
|
|
|
$response->headers->set('Content-Disposition', HeaderUtils::makeDisposition(
|
|
HeaderUtils::DISPOSITION_ATTACHMENT,
|
|
sprintf('%s.geojson', $task->getSlug())
|
|
));
|
|
|
|
return $response;
|
|
}
|
|
|
|
// Renvoie le gpx associé ã une tâche (concrètement il s’agit juste du geojson converti automqtiquement)
|
|
#[Route('/download/{slug}.gpx', name: 'app_task_gpx')]
|
|
public function gpx(Request $request, EntityManagerInterface $entityManager, $slug): Response
|
|
{
|
|
$repository = $entityManager->getRepository(Task::class);
|
|
$task = $repository->findOneBySlug($slug);
|
|
|
|
if (!$task) {
|
|
$this->addFlash('warning', 'Tâche non trouvée !');
|
|
if (!$request->headers->has('Referer')) {
|
|
throw $this->createNotFoundException('Task not found');
|
|
}
|
|
|
|
return $this->redirect($request->headers->get('Referer'));
|
|
}
|
|
|
|
if (!$task->hasGeojson()) {
|
|
return new Response('Not found', 404);
|
|
}
|
|
|
|
$geom = \geoPHP::load($task->getGeojson(), 'json');
|
|
$gpx = $geom->out('gpx');
|
|
|
|
// TODO On ne vérifie rien ici en partant du principe que tout se passe
|
|
// bien. Il doit y avoir des circonstances dans lesquelles ce n’est pas
|
|
// exactement le cas.
|
|
|
|
$response = new Response($gpx);
|
|
|
|
$response->headers->set('Content-Type', 'application/xml');
|
|
$response->headers->set('Content-Disposition', HeaderUtils::makeDisposition(
|
|
HeaderUtils::DISPOSITION_ATTACHMENT,
|
|
sprintf('%s.gpx', $task->getSlug())
|
|
));
|
|
|
|
return $response;
|
|
}
|
|
|
|
// Renvoie le XML OSM associé à la tâche
|
|
#[Route('/download/{slug}.osm', name: 'app_task_osm')]
|
|
public function osm(Request $request, 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'));
|
|
}
|
|
|
|
// On est pas obligé de faire ça maid en le faisant on s’assure que
|
|
// c’est bien du XML valide qu’on envoie.
|
|
$xml = new \DOMDocument();
|
|
$xml->loadXml($task->getOsm());
|
|
|
|
$response = new Response($xml->saveXML());
|
|
|
|
$response->headers->set('Content-Type', 'application/xml');
|
|
$response->headers->set('Content-Disposition', HeaderUtils::makeDisposition(
|
|
HeaderUtils::DISPOSITION_ATTACHMENT,
|
|
sprintf('%s.osm', $task->getSlug())
|
|
));
|
|
|
|
return $response;
|
|
}
|
|
|
|
// Renvoie la liste des tâches du projet sous forme de CSV (ce qui devrait
|
|
// corresponddre à ce que l’on a pu importer)
|
|
#[Route('/download/{slug}.csv', name: 'app_task_csv')]
|
|
public function csv(Request $request, EntityManagerInterface $entityManager, $slug): Response
|
|
{
|
|
$repository = $entityManager->getRepository(Project::class);
|
|
$project = $repository->findOneBySlug($slug);
|
|
|
|
if (!$project) {
|
|
$this->addFlash('warning', 'Projet non trouvé !');
|
|
|
|
return $this->redirect($request->headers->get('referer'));
|
|
}
|
|
|
|
$response = new StreamedResponse();
|
|
|
|
$response->setCallback(function () use ($project): void {
|
|
$output = fopen('php://output', 'a');
|
|
fputcsv(
|
|
$output, [
|
|
'name',
|
|
'description',
|
|
'osm',
|
|
'geojson',
|
|
'status',
|
|
]
|
|
);
|
|
foreach ($project->getTasks() as $task) {
|
|
fputcsv(
|
|
$output,
|
|
[
|
|
$task->getName(),
|
|
$task->getDescription(),
|
|
$task->getOsm(),
|
|
$task->getGeojson(),
|
|
$task->getStatus(),
|
|
]
|
|
);
|
|
}
|
|
fclose($output);
|
|
flush();
|
|
});
|
|
|
|
$response->headers->set('Content-Type', 'text/csv');
|
|
$response->headers->set('Content-Disposition', HeaderUtils::makeDisposition(
|
|
HeaderUtils::DISPOSITION_ATTACHMENT,
|
|
sprintf('%s.csv', $project->getSlug())
|
|
));
|
|
|
|
return $response;
|
|
}
|
|
}
|