feat(mail) : intégration mail OVH IMAP — boîte partagée, lecture, création/lien tâche #5

Merged
matthieu merged 70 commits from feat/mail-integration into develop 2026-05-20 07:45:33 +00:00
Showing only changes of commit c7d12f6acd - Show all commits

View File

@@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
namespace App\Controller\Mail;
use App\Entity\Project;
use App\Entity\Task;
use App\Entity\TaskGroup;
use App\Entity\TaskMailLink;
use App\Entity\TaskPriority;
use App\Repository\MailMessageRepository;
use App\Repository\TaskRepository;
use App\Security\MailAccessChecker;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route('/api/mail/messages/{id}/create-task', name: 'mail_create_task', methods: ['POST'], priority: 1, requirements: ['id' => '\d+'])]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
class MailCreateTaskController extends AbstractController
{
public function __construct(
private readonly MailMessageRepository $messageRepository,
private readonly EntityManagerInterface $em,
private readonly MailAccessChecker $accessChecker,
private readonly TaskRepository $taskRepository,
) {}
public function __invoke(Request $request, int $id): JsonResponse
{
$this->accessChecker->ensureCanAccessMail($this->getUser());
$message = $this->messageRepository->find($id);
if (null === $message) {
throw new NotFoundHttpException('Message not found');
}
$body = json_decode($request->getContent(), true);
$projectId = $body['projectId'] ?? null;
if (null === $projectId) {
throw new UnprocessableEntityHttpException('projectId is required');
}
$project = $this->em->getRepository(Project::class)->find($projectId);
if (null === $project) {
throw new NotFoundHttpException('Project not found');
}
$title = $message->getSubject() ?? 'Mail sans sujet';
if (mb_strlen($title) > 255) {
$title = mb_substr($title, 0, 252).'...';
}
$result = $this->em->wrapInTransaction(function () use ($project, $title, $body, $message) {
$maxNumber = $this->taskRepository->findMaxNumberByProjectForUpdate($project);
$task = new Task();
$task->setProject($project);
$task->setTitle($title);
$task->setNumber($maxNumber + 1);
if (isset($body['taskGroupId']) && null !== $body['taskGroupId']) {
$taskGroup = $this->em->getRepository(TaskGroup::class)->find($body['taskGroupId']);
if (null !== $taskGroup) {
$task->setGroup($taskGroup);
}
}
if (isset($body['priorityId']) && null !== $body['priorityId']) {
$priority = $this->em->getRepository(TaskPriority::class)->find($body['priorityId']);
if (null !== $priority) {
$task->setPriority($priority);
}
}
$this->em->persist($task);
$link = new TaskMailLink();
$link->setTask($task);
$link->setMailMessage($message);
$link->setLinkedAt(new DateTimeImmutable());
$link->setLinkedBy($this->getUser());
$this->em->persist($link);
$this->em->flush();
return $task;
});
return $this->json([
'taskId' => $result->getId(),
'taskNumber' => $result->getNumber(),
'taskTitle' => $result->getTitle(),
'messageId' => $message->getId(),
], 201);
}
}