<?php
namespace App\Controller\Front;
use DateTimeZone;
use Mobile_Detect;
use App\Entity\Message;
use App\Entity\AttachedFile;
use App\Entity\Conversation;
use App\Service\ImageOptimizer;
use App\Mercure\CookieGenerator;
use App\EmailNotification\ToUser;
use App\Entity\Trainee;
use App\Repository\UserRepository;
use Symfony\Component\WebLink\Link;
use App\Repository\CourseRepository;
use App\Service\ConversationService;
use App\Repository\MessageRepository;
use Symfony\Component\Mercure\Update;
use Doctrine\Persistence\ObjectManager;
use Symfony\Component\Mercure\Discovery;
use App\Repository\AttachedFileRepository;
use App\Repository\ConversationRepository;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Authorization;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Intl\Countries;
#[Route(path: '/{_locale}')]
class MessageController extends AbstractController
{
private $translator;
private $imageOptimizer;
public function __construct(TranslatorInterface $translator, ImageOptimizer $imageOptimizer)
{
$this->translator = $translator;
$this->imageOptimizer = $imageOptimizer;
}
private function generateUniqueFileName()
{
return md5(uniqid());
}
#[Route(path: '/messages', name: 'message')]
public function message(ConversationRepository $conversationRepo, ConversationService $conversationService): Response
{
$user = $this->getUser();
$detect = new Mobile_Detect;
if ($detect->isMobile()) {
$conversationsList = $conversationService->userConversationList($user);
if ($conversationsList) {
return $this->render('front/message/responsive-conversation-list.html.twig', [
'conversations' => $conversationsList,
'chat' => false
]);
}
return $this->render('front/message/empty-message.html.twig', [
'chat' => false
]);
}
else {
$recentConversation = $conversationRepo->findLastConversation($user);
if ($recentConversation) {
return $this->redirectToRoute('read_message', [
'token' => $recentConversation->getToken(),
'chat' => false,
]);
}
return $this->render('front/message/empty-message.html.twig', [
'chat' => false
]);
}
}
#[Route(path: '/messages/{token}', name: 'read_message')]
public function readMessage($token, ConversationRepository $conversationRepo, MessageRepository $messageRepo, ConversationService $conversationService, ObjectManager $manager): Response
{
$user = $this->getUser();
$conversation = $conversationRepo->findOneBy(['token' => $token]);
if ($conversation == null) {
return $this->redirectToRoute('message');
}
$groupedMessages = [];
$messages = $conversation->getMessages();
foreach($messages as $message) {
$messageDate = $conversationService->formatMessagesDay($message->getCreatedAt());
//$groupedKey = $message->getCreatedAt()->format('d/m/Y');
$groupedMessages[$messageDate][] = $message;
}
$lastMessage = $conversation->getLastMessage();
if ($lastMessage->getSender() != $user) {
$lastMessage->setIsReaded(true);
}
$manager->persist($lastMessage);
$manager->flush();
$other = $conversationService->getOther($conversation, $user);
$bookings = $conversationService->conversationBookings($conversation);
$options = [
'chat' => false,
'conversation' => $conversation,
'other' => $other,
'bookings' => $bookings,
'type' => $conversationService->userType($other),
'messages' => $conversation->getMessages(),
'groupedMessages' => $groupedMessages,
'menu' => 'messages',
];
$detect = new Mobile_Detect;
if ($detect->isMobile()) {
return $this->render('front/message/responsive-conversation-content.html.twig', $options);
}
else {
$conversationsList = $conversationService->userConversationList($user);
$options['conversations'] = $conversationsList;
return $this->render('front/message/desktop-messaging.html.twig', $options);
}
}
#[Route(path: '/conversations-list', name: 'conversations_list')]
public function conversationsList(ConversationService $conversationService): Response
{
\sleep(1);
$user = $this->getUser();
$conversationsList = $conversationService->userConversationList($user);
if (count($conversationsList) > 0) {
return new JsonResponse([
'conversations' => $conversationsList,
'message' => 'Ok'
]);
}
else {
return new JsonResponse([
'conversations' => $conversationsList,
'message' => $this->translator->trans('messaging.empty_message.no_conversations_yet')
]);
}
}
#[Route(path: '/conversation-content/{token}', name: 'conversation_content')]
public function getConversationContent(Conversation $conversation, CourseRepository $courseRepo, ConversationService $conversationService, AttachedFileRepository $attachedFileRepo, ObjectManager $manager, Request $request): Response
{
$user = $this->getUser();
$lastMessage = $conversation->getLastMessage();
if ($lastMessage->getSender() != $user) {
$lastMessage->setIsReaded(true);
}
$manager->persist($lastMessage);
$manager->flush();
$other = $conversationService->getOther($conversation, $user);
if ($other->getIsPro()) {
$name = $other->getProInfo()->getFullname();
}
else {
$name = $other->getFirstname() .' '. $other->getLastname();
}
$infos = [
'token' => $conversation->getToken(),
'profile' => $other->getPhoto(),
'status' => $other->getStatus(),
'noUser' => $this->translator->trans('messaging.no_user'),
'type' => $conversationService->userType($other),
'name' => $name,
'slug' => $other->getSlug()
];
// get conversation messages, bookings, payement requests and files
$groupedMessages = [];
$files = [];
$booking = null;
$note = null;
//$paymentRequest = null;
$messagesBrut = $conversation->getMessages();
foreach($messagesBrut as $messageBrut) {
$me = false;
$paymentRequest = false;
if ($messageBrut->getSender() == $user ) {
$me = true;
}
$type = 'text';
if ($messageBrut->getType() != null) {
$type = $messageBrut->getType();
}
$messageContent = $messageBrut->getContent();
if ($type == 'files') {
$filesBrut = $attachedFileRepo->findBy(['message' => $messageBrut->getId()]);
foreach($filesBrut as $fileBrut) {
$file = [
'path' => $fileBrut->getPath(),
'name' => $fileBrut->getName(),
'type' => $fileBrut->getType(),
'extension' => $fileBrut->getExtension()
];
$files[] = $file;
}
}
else if ($type == 'booking') {
$bookingBrut = $messageBrut->getBooking();
$messageContent = $this->translator->trans('messaging.'. $messageBrut->getContent());
$showAction = false;
$link = '';
if ($bookingBrut->getStatus() == 'pending' && $user->getIsPro()) {
$showAction = true;
$link = $this->generateUrl('read_message', array('token' => $conversation->getToken()));
}
$booking = [
'course' => $bookingBrut->getCourse()->getTranslatedContent($request->getLocale())->getName(),
'courseToken' => $bookingBrut->getCourse()->getToken(),
'bookingId' => $bookingBrut->getId(),
'photo' => $bookingBrut->getCourse()->getCover() ?? "default.png",
'adult' => $bookingBrut->getAdult(),
'teen' => $bookingBrut->getTeen(),
'child' => $bookingBrut->getChild(),
'total' => $bookingBrut->getTotal(),
'begin' => $bookingBrut->getBegin(),
'end' => $bookingBrut->getEnd(),
'status' => $bookingBrut->getStatus(),
'statusText' => $this->translator->trans('messaging.'. $bookingBrut->getStatus()),
'payment' => $bookingBrut->getPaymentMethod(),
'paid' => $bookingBrut->getIsPaid(),
'showAction' => $showAction,
'link' => $link,
'address' => $bookingBrut->getCourse()->getAddress()->getCity() .", " . Countries::getName($bookingBrut->getCourse()->getAddress()->getCountryCode())
];
}
else if ($type == 'notification') {
$messageContent = $this->translator->trans('messaging.'.$messageBrut->getContent());
if ($messageBrut->getPaymentRequest()) {
$paymentAction = false;
if (!$user->getIsPro()) {
$paymentAction = true;
}
$status = $messageBrut->getPaymentRequest()->getStatus();
$paymentRequest = [
'id' => $messageBrut->getPaymentRequest()->getId(),
'title' => $this->translator->trans('messaging.stripe_payment_request'),
'textButton' => $this->translator->trans('messaging.pay'),
'status' => $status,
'statusText' => $this->translator->trans('messaging.'. $status),
'paymentAction' => $paymentAction,
'link' => '/' . $request->getLocale() . '/prepare-booking-checkout',
];
}
}
else if ($type == 'rate') {
$action = true;
$messageText = $this->translator->trans('messaging.notation_message');
if ($user->getIsPro()) {
$action = false;
$messageText = $this->translator->trans('messaging.rate_sent');
}
$messageContent = $this->translator->trans('messaging.'.$messageBrut->getContent());
$courseToken = $messageBrut->getToRate();
$course = $courseRepo->findOneBy(['token' => $courseToken]);
$note = [
'title' => $this->translator->trans('messaging.notation_title'),
'message' => $messageText,
'course' => $course->getTranslatedContent($request->getLocale())->getName(),
'textBtn' => $this->translator->trans('messaging.notation_btn'),
'showAction' => $action
];
if ($messageBrut->getNote() != null) {
$noteInfo = $messageBrut->getNote();
$note['organizationText'] = $this->translator->trans('messaging.organizationText');
$note['organization'] = $noteInfo->getEducation();
$note['educationText'] = $this->translator->trans('messaging.educationText');
$note['education'] = $noteInfo->getEducation();
$note['commentText'] = $this->translator->trans('messaging.commentText');
$note['comment'] = $noteInfo->getComment();
$note['isNoted'] = true;
}
else {
$note['link'] = $this->generateUrl('trainee_note_pending');
$note['isNoted'] = false;
}
}
$messageDate = $conversationService->formatMessagesDay($messageBrut->getCreatedAt());
//$messageDate = $messageBrut->getCreatedAt()->format('d/m/Y');
/**
* @var DateTime $messageHour
*/
$messageHour = $messageBrut->getCreatedAt();
$messageHour->setTimezone(new DateTimeZone($user->getTimezone()));
if ($messageBrut->getSender() == $user) {
$senderName = 'Vous';
}
else {
if ($messageBrut->getSender()->getIsPro()) {
$senderName = $messageBrut->getSender()->getProInfo()->getFullname();
}
else {
$senderName = $messageBrut->getSender()->getFirstName() .' '. $messageBrut->getSender()->getLastName();
}
}
$message = [
'sender' => $senderName,
'date' => $messageHour->format('H:i'),
'content' => $messageContent,
'me' => $me,
'type' => $type,
'files' => $files,
'booking' => $booking,
'paymentRequest' => $paymentRequest,
'note' => $note
];
$groupedMessages[$messageDate][] = $message;
}
return new JsonResponse([
'infos' => $infos,
'groupedMessages' => $groupedMessages
]);
}
#[Route(path: '/publish', name: 'publish')]
public function publish(HubInterface $hub, Request $request)
{
$update = new Update(
'http://example.com/ping',
json_encode(['status' => 'Reussite'])
);
$hub($update);
return new Response('published!');
}
#[Route(path: '/send-message', name: 'send_message')]
public function sendMessage(ConversationRepository $conversationRepo, ObjectManager $manager, MessageBusInterface $bus, HubInterface $hub, ConversationService $conversationService, ToUser $toUser, Request $request)
{
$user = $this->getUser();
if (isset($_POST['conversationId'])) {
$conversationId = $_POST['conversationId'];
$messageContent = $_POST['message'];
$countFiles = $_POST['countedFiles'];
}
else {
$data = json_decode($request->getContent(), true);
$conversationId = $data['conversationId'];
$messageContent = $data['messageContent'];
$countFiles = 0;
}
$filesToReturn = [];
$conversation = $conversationRepo->find($conversationId);
$date = new \Datetime;
$other = $conversationService->getOther($conversation, $user);
$oldLastActivity = $conversation->getLastActivity();
$message = new Message();
$message->setContent($messageContent)
->setIsReaded(false)
->setSender($user)
->setConversation($conversation)
->setCreatedAt($date);
if ($countFiles > 0) {
$message->setType('files');
$files = $_FILES;
foreach ($files as $file) {
$originalName = $file['name'];
$ext = pathinfo($originalName, PATHINFO_EXTENSION);
$fileName = $this->generateUniqueFileName() . '.' . $ext;
$directory = $this->getParameter('attached_files');
if (str_contains($file['type'], 'image')) {
$type = 'image';
$this->imageOptimizer->resizeImageFull($file["tmp_name"], $fileName, 800, 800, 'attached_files');
}
else {
$type = 'other';
move_uploaded_file($file["tmp_name"], "$directory/" . $fileName);
}
$file = new AttachedFile;
$file->setPath($fileName)
->setName($originalName)
->setType($type)
->setExtension($ext)
->setMessage($message);
$manager->persist($file);
$filesToReturn[] = [
'path' => $fileName,
'name' => $originalName,
'type' => $type,
'extension' => $ext
];
}
}
$manager->persist($message);
$conversation->setLastMessage($message)
->setLastActivity($date);
$manager->persist($conversation);
$manager->flush();
$interval = abs($date->getTimestamp() - $oldLastActivity->getTimestamp()) / 60;
if ($interval > 5 ) {
// Send email to other
$toUser->newMessage($other, $user, $message->getContent(), $conversation->getToken());
}
$diff = $date->diff($oldLastActivity);
$days = $diff->format('%a');
if ($days > 0) {
$sameDate = false;
}
else {
$sameDate = true;
}
$messageDate = $conversationService->formatMessagesDay($date);
$timeSender = clone $date;
$timeRecipient = clone $date;
$timeSenderFormatted = $timeSender->setTimezone(new DateTimeZone($user->getTimezone()));
$timeRecipientFormatted = $timeRecipient->setTimezone(new DateTimeZone($other->getTimezone()));
$messageToReturn = [
'sender' => 'Vous',
'content' => $messageContent,
'date' => $timeSenderFormatted->format('H:i'),
'type' => $message->getType(),
'files' => $filesToReturn,
'sameDate' => $sameDate,
'messageDate' => $messageDate
];
// Publish to mercure
$messageToPublish = [
'sender' => $user->getFirstname() .' '. $user->getLastname(),
'content' => $messageContent,
'date' => $timeRecipientFormatted->format('H:i'),
'conversation' => $conversation->getId(),
'token' => $conversation->getToken(),
'type' => $message->getType(),
'files' => $filesToReturn,
'sameDate' => $sameDate,
'messageDate' => $message
];
$recipient = $other->getId();
$url = "http://example.com/books";
if(str_contains($request->getHost(), "racket-trip.com")) {
$url = "http://example-2.com/books";
}
$update = new Update(
$url . "/" . $recipient,
json_encode($messageToPublish)
);
try {
$bus->dispatch($update);
} catch (\Throwable $th) {
//throw $th;
}
return new JsonResponse(['message' => $messageToReturn]);
}
#[Route(path: '/attach-files', name: 'attach_files')]
public function attachFiles(ObjectManager $manager, MessageRepository $messageRepo)
{
$message = $messageRepo->find($_POST['messageId']);
$files = $_FILES;
$filesToReturn = [];
foreach ($files as $file) {
$originalName = $file['name'];
$ext = pathinfo($originalName, PATHINFO_EXTENSION);
if (str_contains($file['type'], 'image')) {
$type = 'image';
}
else {
$type = 'other';
}
$fileName = $this->generateUniqueFileName() . '.' . $ext;
$directory = $this->getParameter('attached_files');
$isFileUploaded = move_uploaded_file($file["tmp_name"], "$directory/" . $fileName);
if ($isFileUploaded) {
$file = new AttachedFile;
$file->setPath($fileName)
->setName($originalName)
->setType($type)
->setExtension($ext)
->setMessage($message);
$manager->persist($file);
$filesToReturn[] = [
'path' => $fileName,
'name' => $originalName,
'type' => $type,
'extension' => $ext
];
}
}
$manager->flush();
$messageWithFiles = [
'id' => $message->getId(),
'sender' => 'Vous',
'content' => $message->getContent(),
'date' => $message->getCreatedAt()->format('H:i'),
'files' => $filesToReturn
];
return new JsonResponse($messageWithFiles);
}
#[Route('/discover', name: 'discover')]
public function discover(Request $request, Discovery $discovery, Authorization $authorization)
{
// Link: <https://hub.example.com/.well-known/mercure>; rel="mercure"
$discovery->addLink($request);
$authorization->setCookie($request, ['*']);
return $this->json([true]);
}
/* Old Discover
#[Route(path: '/discover', name: 'discover')]
public function discover(Request $request, CookieGenerator $cookieGenerator)
{
$user = $this->getUser();
// This parameter is automatically created by the MercureBundle
$hubUrl = $this->getParameter('mercure.default_hub');
//$hubUrl = "https://localhost:3000/.well-known/mercure";
// Link: <http://localhost:3000/.well-known/mercure>; rel="mercure"
$this->addLink($request, new Link('mercure', $hubUrl));
$response = $this->json('Done !');
$cookie = $cookieGenerator->generate($user);
$response->headers->setCookie($cookie);
return $response;
}
*/
#[Route(path: '/contact-pro/{from}', name: 'contact_pro')]
public function contactPro($from, UserRepository $userRepo, ConversationService $conversationService, ObjectManager $manager, TranslatorInterface $translator, ToUser $toUser): Response
{
$trainee = $this->getUser();
if (isset($_POST['contact'])) {
$proId = $_POST['user_id'];
$pro = $userRepo->find($proId);
$messageContent = $_POST['message'];
$conversation = $conversationService->conversationBetween($trainee, $pro);
$message = new Message;
$message->setSender($trainee)
->setContent($messageContent)
->setCreatedAt(new \Datetime)
->setIsReaded(false)
->setConversation($conversation);
$conversation->setLastMessage($message)
->setLastActivity(new \DateTime());
$manager->persist($conversation);
$manager->persist($message);
$manager->flush();
$this->addFlash(
'success',
$translator->trans("flashes.message_controller.message_send")
);
$toUser->newMessage($pro, $trainee, $messageContent, $conversation->getToken());
if ($from == 'pro') {
return $this->redirectToRoute('view_pro', [
'type' => $conversationService->userType($pro),
'slug' => $pro->getSlug()
]);
}
else {
return $this->redirectToRoute('view_course', [
'token' => $from
]);
}
}
return $this->render('front/message/desktop-messaging.html.twig');
}
}