<?php
namespace App\Controller;
use App\Entity\FileMessage;
use App\Entity\Message;
use App\Entity\MessageReaction;
use App\Entity\Mission;
use App\Entity\WorkflowStep;
use App\Entity\Campaign;
use App\Enum\AdminMail;
use App\Enum\Role;
use App\Event\User\UserReactionEvent;
use App\Form\MessageType;
use App\Form\MissionParticipantDelaisType;
use App\Repository\MessageReactionRepository;
use App\Repository\MessageRepository;
use App\Repository\MissionRepository;
use App\Repository\UserRepository;
use App\Repository\WorkflowStepRepository;
use App\Repository\CampaignRepository;
use App\Repository\FileMessageRepository;
use App\Repository\MissionParticipantRepository;
use App\Service\MessageService;
use App\Service\NotificationService;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Mime\Address;
use Twig\Environment;
use App\Service\GoogleStorageService;
use App\Service\MissionParticipantService;
use App\Service\PriceService;
#[Route('/messages', name: 'messages')]
class SycMessagesAndPlanning extends AbstractController
{
public function __construct(
private Environment $twig,
private EntityManagerInterface $entityManagerInterface,
private Filesystem $filesystem,
private MessageService $messageService,
private LoggerInterface $logger,
private MessageRepository $messageRepository,
private MessageReactionRepository $messageReactionRepository,
private EventDispatcherInterface $dispatcher,
private NotificationService $notificationService,
private MissionParticipantService $missionParticipantService,
private UserRepository $userRepository,
private GoogleStorageService $googleStorageService,
private MissionParticipantRepository $missionParticipantRepository,
private PriceService $priceService
) {
}
#[Route('/last/{lastIdMessage}-{nbMessage}', name: 'last')]
public function lastMessages(Request $request,FileMessageRepository $fileMessageRepository, MissionRepository $missionRepository, string $lastIdMessage, MessageRepository $messageRepository,UserRepository $userRepository, $nbMessage = 10)
{
if ($this->getUser() == null) {
return new JsonResponse([
'status' => "KO",
], Response::HTTP_UNAUTHORIZED);
}
$unredMessage = $request->query->get('unred-message');
$missionId = $request->query->get('mission-id');
$currentRouteName=$request->query->get('current-route-name');
$estimatedIncome = $request->query->get('estimatedIncome');
$timeZone = $request->query->get('time-zone');
$dateOffSetHour = floatval($request->query->get('date-offset-hour'));
$isLoadMore = $request->query->get('is-load-more');
$lastIdMessage = preg_replace("/message/", "", $lastIdMessage);
$messageIdsDeleted = [] ;
$fileMessageIdsDeleted = [];
$messagesEditing = [];
$messagesWithReactions = [];
$mission = $missionRepository->findOneBy(["id" => $missionId]);
$listOfUserTyping = $this->messageService->getUserTypingWithoutCurrentUser($mission);
$userTypingWithDuration = $this->messageService->calculaTimeTypingOfEachUser($listOfUserTyping);
if(is_null($userTypingWithDuration)){
$userTypingWithDuration = [];
}
$message = $messageRepository->findOneBy(['id' => $lastIdMessage]);
if ($mission != null) {
if($isLoadMore == "true" || $message == null){
$listLastMessage = $messageRepository->getAllMessage($mission->getCampaign(),$nbMessage);
}
else{
$listLastMessage =$messageRepository->findLastMessage($mission->getCampaign(), $message->getCreatedAt());
$messageIdsDeleted = $messageRepository->findIdsMessageDeleted($mission->getCampaign()) ;
$fileMessageIdsDeleted = $fileMessageRepository->findIdsFileMessageDeleted($mission->getCampaign());
$messagesEditing= $messageRepository->findMessagesEditing($mission->getCampaign()) ;
$messagesWithReactions= $messageRepository->findMessageWithReaction($mission->getCampaign()) ;
}
$nbAllMessage = $messageRepository->nbAllMessage($mission->getCampaign())['nb'];
usort($listLastMessage, function($a, $b) {
$dateA = ($a->getCreatedAt()->getTimestamp());
$dateB = ($b->getCreatedAt()->getTimestamp());
return $dateA - $dateB ;
});
$missionParticipant = null;
foreach ($mission->getParticipants() as $participant) {
if ($participant->getUser()->getId() == $this->getUser()->getId()) {
$missionParticipant = $participant;
}
}
$lastTenMessages = $messageRepository->getLastTenMessages($mission->getCampaign());
$listIdsLastMessage = $this->getIdsMessage($lastTenMessages);
$futuresActions = $this->missionParticipantService->getUniqByFutureActions($mission->getCampaign()->getId(),$this->getUser()->getId());
return new JsonResponse([
"status" => "ok",
"html" => $this->getHtmlToRenderInView(mission: $mission, listLastMessage: $listLastMessage, timeZone: $timeZone, dateOffSetHour: $dateOffSetHour, unredMessage: $unredMessage,nbAllMessage: $nbAllMessage),
"lists_last_message_id"=>$listIdsLastMessage['message_ids'],
"lists_last_file_message_id"=>$listIdsLastMessage['file_message_ids'],
"planning" => $this->twig->render("mission/_planning_item.html.twig", [
"mission" => $mission,
"campaign" => $mission->getCampaign(),
"userConnected" => $this->getUser(),
"missionParticipant" => $missionParticipant,
]),
"list_user_read_last_message"=>$this->userReadLastMessage($messageRepository->findOneBy(['campaign' => $mission->getCampaign()], ['createdAt' => 'DESC'])),
"message_deleted_ids"=> $messageIdsDeleted,
'user_typing' => $this->getUserTypingMessage($userTypingWithDuration),
"file_message_deleted_ids"=> $fileMessageIdsDeleted,
"messages_editing"=>$messagesEditing,
"messages_with_reactions"=>$this->getElementReactions($messagesWithReactions),
"futureActions" => $this->getHtmlFutureActions($mission, $futuresActions, $currentRouteName, $estimatedIncome),
]);
}
// $this->sendEmailError('-', '-', 'L\'id de la mission est null');
return new JsonResponse(["status" => "ko"], 200);
}
#[Route('/delete/{id}', name: 'detele_message', methods: ['POST'])]
public function deleteMessages(Message $message)
{
if ($message->getUser()->getId() == $this->getUser()->getId() || $this->isGranted('ROLE_ADMIN')) {
$message->setIsDeleted(true);
$this->entityManagerInterface->flush();
return new JsonResponse([
'status' => "ok"
]);
}
return new JsonResponse(['status' => "ko", Response::HTTP_UNAUTHORIZED]);
}
#[Route('/delete/file/{id}', name: 'detele_file_message', methods: ['POST'])]
public function deleteFileMessages(FileMessage $fileMessage, GoogleStorageService $googleStorageService)
{
if ($fileMessage->getMessages()->getUser()->getId() == $this->getUser()->getId() || $this->isGranted('ROLE_ADMIN')) {
$message = $fileMessage->getMessages();
//delete file if in storage
$company = $message->getCampaign()->getCompany();
$campaign = $message->getCampaign();
$bucketName = "company-" . strtolower($company->getId());
$fileName = "Campaigns/" . $campaign->getId() . "/" . $fileMessage->getName();
//end delete in google storage
$fileMessage->setIsDeleted(true);
$this->entityManagerInterface->flush();
$file = $this->messageService->getFileMissionUrl($fileMessage->getMessages()->getMission(), $fileMessage->getName());
if ($message->getContent() == null && $fileMessage->getMessages()->getFileMessages()!=null && $this->messageService->allFileMessageIsDeleted($fileMessage->getMessages())) {
$message->setIsDeleted(true);
$this->entityManagerInterface->flush();
}
try {
$this->filesystem->remove($file);
} catch (\Throwable $th) {
$this->logger->error("can't delete file in $file");
}
$googleStorageService->deleteFile($bucketName, $fileName);
return new JsonResponse([
'status' => "ok"
]);
}
return new JsonResponse(['status' => "ko", Response::HTTP_UNAUTHORIZED]);
}
#[Route('/emoji/toggle/{id}', name: 'toggle_emoji_reaction', methods: ['POST', 'GET'])]
public function toggleEmojiReaction(Message $message, Request $request): JsonResponse
{
$user = $this->getUser();
if ($user != null) {
$emoji = $request->request->get('emoji');
$emojiId = $request->request->get('emojiId');
$campaignOfMessage = $message->getCampaign();
$messageReaction = $this->messageReactionRepository->findReactionByUser(messageId: $message->getId(), userId: $user->getId(), emojiId: $emojiId);
if ($messageReaction != null) {
$message->removeMessageReaction($messageReaction[0]);
$this->entityManagerInterface->remove($messageReaction[0]);
$this->entityManagerInterface->flush();
} else {
$newReaction = new MessageReaction();
$newReaction->setEmoji($emoji);
$newReaction->setUser($user);
$newReaction->setEmojiId(intval($emojiId));
$newReaction->setMessage($message);
$message->addMessageReaction($newReaction);
$this->entityManagerInterface->persist($newReaction);
$this->entityManagerInterface->flush();
$event = new UserReactionEvent($message->getUser(), $this->getUser(), $message, $newReaction->getEmoji(),$campaignOfMessage);
$this->dispatcher->dispatch($event, UserReactionEvent::NAME);
}
return new JsonResponse([
'status' => "ok",
'id_user' => $user->getId()
]);
}
return new JsonResponse(['status' => "ko", Response::HTTP_UNAUTHORIZED]);
}
#[Route('/send/error', name: 'send_error', methods: ['POST'])]
public function sendError(Request $request)
{
$this->messageService->sendEmailError($request->request->get('xhr'), $request->request->get('type'), $request->request->get('message'));
return new JsonResponse([]);
}
#[Route('/add/{id}', name: '_add', methods: ['POST'])]
public function addMessage(Request $request, Mission $mission, MessageRepository $messageRepository)
{
$message = new Message();
$entityManager = $this->getDoctrine()->getManager();
$path = 'file_message_directory';
$form = $this->createForm(MessageType::class, $message);
$form->handleRequest($request);
$error_id_message = $request->query->get('error_id_message', null);
$messageInsered = $messageRepository->findOneBy(['id' => $error_id_message]);
if ($form->isSubmitted() && $form->isValid() && $messageInsered == null or ($path = 'file_message_directory')) {
$errorMessage = "";
$errorCode = 200;
try {
//$files = $form->get('fileMessages')->getData();
$drapDrop = $request->query->get('drag-drop');
$files = !is_null($request->query->get('drag-drop')) && $request->query->get('drag-drop') == 1 ? $request->files->get('file') : $form->get('fileMessages')->getData();
if ($files != null && sizeof($files) > 0 || !empty(preg_replace("/\s/", "", $message->getContent()))) {
foreach ($files as $file) {
$nameSplitByDot = explode('.', $file->getClientOriginalName());
$extension = $nameSplitByDot != null && sizeof($nameSplitByDot) > 1 ? end($nameSplitByDot) : $file->guessExtension();
$type = preg_replace('/\/[a-zA-Z-_,.]*\s?/',"",$file->getMimeType());
$nameUniqueCompany = strtolower("company-" . $mission->getCampaign()->getCompany()->getId());
$destination = $this->getParameter($path) . '/' . $mission->getId() . '/message';
$originalFilename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
$newFilename = $originalFilename . '-' . uniqid() . '.' . $extension;
//upload file in google storage
$this->googleStorageService->uploadFile($nameUniqueCompany, $file, $newFilename, 'Campaigns/' . $mission->getCampaign()->getId());
$fileMessage = new FileMessage();
$fileMessage->setName($newFilename);
$fileMessage->setIsNew(1);
$fileMessage->setType($type);
$message->addFileMessage($fileMessage);
}
$idMessage = $request->query->get('id-message');
$replyTo = $request->query->get('id-reply-message');
if ($idMessage != "") {
$messagePersisted = $messageRepository->findOneBy(['id' => $idMessage]);
$messagePersisted->setIsModified(true);
$messagePersisted->setContent($message->getContent(), false);
$message = $messagePersisted;
} else {
$message->setUser($this->getUser())
->setCampaign($mission->getCampaign())
->setMission(mission: $mission)
->setUserListWhoRead([$this->getUser()->getId()]);
$entityManager->persist($message);
}
if ($replyTo != "") {
$replyedMessage = $messageRepository->findOneBy(['id' => $replyTo]);
$message->setReplyToWithMessage($replyedMessage);
}
$entityManager->flush();
$this->messageService->sendEmailForParticipantMentionedInTchat($mission, $message);
}else{
$errorMessage = "Empty content";
}
} catch (\Throwable $e) {
$errorMessage = "throwable : " . $e->getMessage();
$errorCode = 500;
$this->messageService->sendEmailError('exception', $e->getMessage(), "message : {$message->getContent()} mission : {$mission->getId()}");
} catch (\Exception $e) {
$errorMessage = "exception : " . $e->getMessage();
$errorCode = 500;
$this->messageService->sendEmailError('error', $e->getMessage(), "message : {$message->getContent()} mission: {$mission->getId()}");
} catch (\Error $e) {
$errorMessage = "error: " . $e->getMessage();
$errorCode = 500;
$this->messageService->sendEmailError('error', $e->getMessage(), "message : {$message->getContent()} mission: {$mission->getId()}");
}
return new JsonResponse([
'status' => $errorMessage == "" ? 'ok' : 'ko',
'message_id' => $message->getId(),
'message' => $errorMessage == "" ? 'Enregistrer avec succes ' : $errorMessage,
], $errorCode);
}
return new JsonResponse([
'status' => 'ko',
'message_id' => $message->getId(),
'message' => 'form invalide',
], Response::HTTP_UNAUTHORIZED);
}
#[Route('/addaudio/{id}', name: '_add_audio_file', methods: ['POST'])]
public function addAudioMessageFile(Mission $mission,Request $request, MessageRepository $messageRepository)
{
$message = new Message();
$entityManager = $this->getDoctrine()->getManager();
$file = $request->files->get('audio');
$nameSplitByDot = explode('.', $file->getClientOriginalName());
$extension = $nameSplitByDot && sizeof($nameSplitByDot) > 1 ? end($nameSplitByDot) : $file->guessExtension();
$type = preg_replace('/\/[a-zA-Z-_,.]*\s?/',"",$file->getMimeType());
$nameUniqueCompany = strtolower("company-" . $mission->getCampaign()->getCompany()->getId());
$newFilename = 'audio-' . uniqid() . '.' . $extension;
$this->googleStorageService->uploadFile($nameUniqueCompany, $file, $newFilename, 'Campaigns/' . $mission->getCampaign()->getId());
$fileMessage = new FileMessage();
$fileMessage->setName($newFilename);
$fileMessage->setIsNew(1);
$fileMessage->setType($type);
$message->addFileMessage($fileMessage);
$message->setUser($this->getUser())
->setCampaign($mission->getCampaign())
->setMission(mission: $mission)
->setUserListWhoRead([$this->getUser()->getId()]);
$entityManager->persist($message);
$entityManager->flush();
return new JsonResponse('ok');
}
#[Route('/planning/validation/{type}/{id}/{stepId}', name: '_planning_validation', methods: ['GET'])]
public function stepValidation(string $type, string $stepId, WorkflowStepRepository $workflowStepRepository, Mission $mission)
{
return new JsonResponse([
'html' => $this->getHtmlValidationStep($type, $mission, $workflowStepRepository->findOneBy(['id' => $stepId]))
]);
}
private function getHtmlValidationStep(string $type, Mission $mission, WorkflowStep $step)
{
return $this->twig->render('mission/_validation_step.html.twig', [
'type' => $type,
'mission' => $mission,
'step' => $step
]);
}
private function getHtmlToRenderInView(?Mission $mission, array $listLastMessage, string $timeZone, float $dateOffSetHour, bool|null $unredMessage = false,int $nbAllMessage =0)
{
if (sizeof($listLastMessage) == 0) {
return "";
}
return $this->twig->render("mission/_chat_item.html.twig", [
"messages" => $listLastMessage,
"nbAllMessage" => $nbAllMessage,
"mission" => $mission,
"unred_message" => $unredMessage,
"time_zone" => $timeZone,
"date_off_setHour" => abs($dateOffSetHour),
]);
}
private function getHtmlFutureActions (Mission $mission, array $futuresActions, ?string $currentRouteName=null, ?string $estimatedIncome=null)
{
$formMissionInitialTimeManually = $this->createForm(MissionParticipantDelaisType::class);
return $this->twig->render("mission/Ux/_futures_actions.html.twig", [
'futurActions'=> $futuresActions,
'mission'=> $mission,
'price_service' => $this->priceService,
'campaign'=> $mission->getCampaign(),
'currentRouteName'=>$currentRouteName,
'estimatedIncome'=> $this->getEstimatedIncome(),
'formMissionInitialTimeManually' => $formMissionInitialTimeManually->createView(),
]);
}
private function getEstimatedIncome(){
if ($this->isGranted(Role::ROLE_ADMIN->value)) {
$priceCampaign = $this->missionParticipantRepository->findByInitialTime();
$estimatedIncome = [];
$initialTime = [];
$price = [];
foreach ($priceCampaign as $value) {
if (!isset($estimatedIncome[$value->getMission()->getId()])) {
$estimatedIncome[$value->getMission()->getId()] = [];
}
$initialTime[$value->getMission()->getId()][] = $value->getInitialTime();
$price[$value->getMission()->getId()][] = $value->getEstimatedIncome();
}
} elseif ($this->isGranted(Role::ROLE_SUBCONTRACTOR->value)) {
$visibleCampaign = ['orderedBy' => $this->getUser()];
$estimatedIncome = $this->missionParticipantRepository->getSubcontractorForUserAndMission($this->getUser());
} else {
$estimatedIncome = null;
}
return $estimatedIncome;
}
private function getIdsMessage(array $listLastMessage){
$ids = [];
$fileIds = [];
foreach ($listLastMessage as $key => $message) {
$ids =[...$ids, $message->getId()];
foreach ($message->getFileMessages() as $fileMessage) {
$fileIds =[...$fileIds, $fileMessage->getId()];
}
}
return [
'message_ids'=>$ids,
'file_message_ids'=> $fileIds
];
}
private function getUserTypingMessage(array $user_typing){
return $this->twig->render('services/messages/user_typing_message.html.twig',[
'user_typing' => $user_typing
]);
}
private function getElementReactions(array $messages){
$listMessageWithReactions = [];
foreach ($messages as $key => $message) {
$listMessageWithReactions = [... $listMessageWithReactions,[
'id'=> $message->getId(),
'content'=> $this->messageService->getReactionsHtml($message)
]];
}
return $listMessageWithReactions ;
}
private function userReadLastMessage(?Message $message=null){
$userIds = [];
$users = [];
if($message!=null){
$userIds = $message->getUserListWhoRead();
$userIds = array_diff($userIds, [$message->getUser()->getId()]);
$users = $this->userRepository->findByIds($userIds);
}
return [
'html'=>$this->twig->render('services/messages/users_who_read.html.twig',[
'user_list' => $users
]),
'ids'=>[...$userIds],
]
;
}
function splitChar($string, $lg_max)
{
if (strlen($string) > $lg_max) {
$string = substr($string, 0, $lg_max);
$last_space = strrpos($string, " ");
$string = substr($string, 0, $last_space) . "...";
}
return $string;
}
#[Route('/user/istyping/{id}',name:'user_is_typing')]
public function setUserIsTyping(Campaign $campaign,CampaignRepository $campaignRepository){
$this->messageService->postUserTypingMessage($campaign->getId());
return new JsonResponse(['status'=>'ok']);
}
}