vendor/uvdesk/core-framework/Controller/Thread.php line 44

Open in your IDE?
  1. <?php
  2. namespace Webkul\UVDesk\CoreFrameworkBundle\Controller;
  3. use Symfony\Component\HttpFoundation\Request;
  4. use Symfony\Component\HttpFoundation\Response;
  5. use Symfony\Component\EventDispatcher\GenericEvent;
  6. use Webkul\UVDesk\CoreFrameworkBundle\Entity\Ticket;
  7. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  8. use Webkul\UVDesk\CoreFrameworkBundle\Entity\Attachment;
  9. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  10. use Webkul\UVDesk\CoreFrameworkBundle\Entity\TicketStatus;
  11. use Webkul\UVDesk\CoreFrameworkBundle\Entity\Thread as TicketThread;
  12. use Webkul\UVDesk\CoreFrameworkBundle\Workflow\Events as CoreWorkflowEvents;
  13. use Webkul\UVDesk\CoreFrameworkBundle\Services\UserService;
  14. use Symfony\Contracts\Translation\TranslatorInterface;
  15. use Webkul\UVDesk\CoreFrameworkBundle\Services\UVDeskService;
  16. use Webkul\UVDesk\CoreFrameworkBundle\Services\TicketService;
  17. use Webkul\UVDesk\CoreFrameworkBundle\Services\EmailService;
  18. use Symfony\Component\HttpKernel\KernelInterface;
  19. use Webkul\UVDesk\CoreFrameworkBundle\Services\FileUploadService;
  20. class Thread extends AbstractController
  21. {
  22.     private $userService;
  23.     private $translator;
  24.     private $eventDispatcher;
  25.     private $ticketService;
  26.     private $emailService;
  27.     private $kernel;
  28.     private $fileUploadService;
  29.     public function __construct(UserService $userServiceTranslatorInterface $translatorTicketService $ticketServiceEmailService $emailServiceEventDispatcherInterface $eventDispatcherKernelInterface $kernelFileUploadService $fileUploadService)
  30.     {
  31.         $this->kernel $kernel;
  32.         $this->userService $userService;
  33.         $this->emailService $emailService;
  34.         $this->translator $translator;
  35.         $this->ticketService $ticketService;
  36.         $this->eventDispatcher $eventDispatcher;
  37.         $this->fileUploadService $fileUploadService;
  38.     }
  39.     public function saveThread($ticketIdRequest $request)
  40.     {
  41.         $params $request->request->all();
  42.         $entityManager $this->getDoctrine()->getManager();
  43.         $ticket $entityManager->getRepository(Ticket::class)->findOneById($ticketId);
  44.         // Proceed only if user has access to the resource
  45.         if (false == $this->ticketService->isTicketAccessGranted($ticket)) {
  46.             throw new \Exception('Access Denied'403);
  47.         }
  48.         if (empty($ticket)) {
  49.             throw new \Exception('Ticket not found'404);
  50.         } else if ('POST' !== $request->getMethod()) {
  51.             throw new \Exception('Invalid Request'403);
  52.         }
  53.         if (empty($params)) {
  54.             return $this->redirect($request->headers->get('referer') ?: $this->generateUrl('helpdesk_member_ticket_collection'));
  55.         } else if ('note' == $params['threadType'] && false == $this->userService->isAccessAuthorized('ROLE_AGENT_ADD_NOTE')) {
  56.             // Insufficient user privilege to create a note
  57.             throw new \Exception('Insufficient Permisions'400);
  58.         }
  59.         // // Deny access unles granted ticket view permission
  60.         // $this->denyAccessUnlessGranted('AGENT_VIEW', $ticket);
  61.         // Check if reply content is empty
  62.         $parsedMessage trim(strip_tags($params['reply'], '<img>'));
  63.         $parsedMessage str_replace('&nbsp;'''$parsedMessage);
  64.         $parsedMessage str_replace(' '''$parsedMessage);
  65.         if (null == $parsedMessage) {
  66.             $this->addFlash('warning'$this->translator->trans('Reply content cannot be left blank.'));
  67.         }
  68.         // @TODO: Validate file attachments
  69.         // if (true !== $this->get('file.service')->validateAttachments($request->files->get('attachments'))) {
  70.         //     $this->addFlash('warning', "Invalid attachments.");
  71.         // }
  72.         
  73.         // $adminReply =  str_replace(['<p>','</p>'],"",$params['reply']);
  74.         $threadDetails = [
  75.             'user' => $this->getUser(),
  76.             'createdBy' => 'agent',
  77.             'source' => 'website',
  78.             'threadType' => strtolower($params['threadType']),
  79.             'message' => str_replace(['&lt;script&gt;''&lt;/script&gt;'], ''htmlspecialchars($params['reply'])),
  80.             'attachments' => $request->files->get('attachments')
  81.         ];
  82.         if(!empty($params['status'])){
  83.             $ticketStatus $entityManager->getRepository(TicketStatus::class)->findOneByCode($params['status']);
  84.             $ticket->setStatus($ticketStatus);
  85.         }
  86.         if (isset($params['to'])) {
  87.             $threadDetails['to'] = $params['to'];
  88.         }
  89.         if (isset($params['cc'])) {
  90.             $threadDetails['cc'] = $params['cc'];
  91.         }
  92.         if (isset($params['cccol'])) {
  93.             $threadDetails['cccol'] = $params['cccol'];
  94.         }
  95.         if (isset($params['bcc'])) {
  96.             $threadDetails['bcc'] = $params['bcc'];
  97.         }
  98.         // Create Thread
  99.         $thread $this->ticketService->createThread($ticket$threadDetails);
  100.         // $this->addFlash('success', ucwords($params['threadType']) . " added successfully.");
  101.         // @TODO: Remove Agent Draft Thread
  102.         // @TODO: Trigger Thread Created Event
  103.         // @TODO: Cross Review
  104.         // check for thread types
  105.         switch ($thread->getThreadType()) {
  106.             case 'note':
  107.                 $event = new CoreWorkflowEvents\Ticket\Note();
  108.                 $event
  109.                     ->setTicket($ticket)
  110.                     ->setThread($thread)
  111.                 ;
  112.                 $this->eventDispatcher->dispatch($event'uvdesk.automation.workflow.execute');
  113.                 // @TODO: Render response on the basis of event response (if propogation was stopped or not)
  114.                 $this->addFlash('success'$this->translator->trans('Note added to ticket successfully.'));
  115.                 break;
  116.             case 'reply':
  117.                 $event = new CoreWorkflowEvents\Ticket\AgentReply();
  118.                 $event
  119.                     ->setTicket($ticket)
  120.                     ->setThread($thread)
  121.                 ;
  122.                 $this->eventDispatcher->dispatch($event'uvdesk.automation.workflow.execute');
  123.                 // @TODO: Render response on the basis of event response (if propogation was stopped or not)
  124.                 $this->addFlash('success'$this->translator->trans('Success ! Reply added successfully.'));
  125.                 break;
  126.             case 'forward':
  127.                 // Prepare headers
  128.                 $headers = ['References' => $ticket->getReferenceIds()];
  129.                 if (null != $ticket->currentThread->getMessageId()) {
  130.                     $headers['In-Reply-To'] = $ticket->currentThread->getMessageId();
  131.                 }
  132.                 // Prepare attachments
  133.                 $attachments $entityManager->getRepository(Attachment::class)->findByThread($thread);
  134.                 $projectDir $this->kernel->getProjectDir();
  135.                 $attachments array_map(function($attachment) use ($projectDir) {
  136.                 return str_replace('//''/'$projectDir "/public" $attachment->getPath());
  137.                 }, $attachments);
  138.                 $message '<html><body style="background-image: none"><p>'.html_entity_decode($thread->getMessage()).'</p></body></html>';
  139.                 // Forward thread to users
  140.                 try {
  141.                     $messageId $this->emailService->sendMail($params['subject'] ?? ("Forward: " $ticket->getSubject()), $message$thread->getReplyTo(), $headers$ticket->getMailboxEmail(), $attachments ?? [], $thread->getCc() ?: [], $thread->getBcc() ?: []);
  142.     
  143.                     if (!empty($messageId)) {
  144.                         $thread->setMessageId($messageId);
  145.     
  146.                         $entityManager->persist($createdThread);
  147.                         $entityManager->flush();
  148.                     }
  149.                 } catch (\Exception $e) {
  150.                     // Do nothing ...
  151.                     // @TODO: Log exception
  152.                 }
  153.                 // @TODO: Render response on the basis of event response (if propogation was stopped or not)
  154.                 $this->addFlash('success'$this->translator->trans('Reply added to the ticket and forwarded successfully.'));
  155.                 break;
  156.             default:
  157.                 break;
  158.         }
  159.         // Check if ticket status needs to be updated
  160.         $updateTicketToStatus = !empty($params['status']) ? (trim($params['status']) ?: null) : null;
  161.         if (!empty($updateTicketToStatus) && $this->userService->isAccessAuthorized('ROLE_AGENT_UPDATE_TICKET_STATUS')) {
  162.             $ticketStatus $entityManager->getRepository(TicketStatus::class)->findOneById($updateTicketToStatus);
  163.             if (!empty($ticketStatus) && $ticketStatus->getId() === $ticket->getStatus()->getId()) {
  164.                 $ticket->setStatus($ticketStatus);
  165.                 $entityManager->persist($ticket);
  166.                 $entityManager->flush();
  167.                 // @TODO: Trigger Ticket Status Updated Event
  168.             }
  169.         }
  170.         // Redirect to either Ticket View | Ticket Listings
  171.         if ('redirect' === $params['nextView']) {
  172.             return $this->redirect($this->generateUrl('helpdesk_member_ticket_collection'));
  173.         }
  174.         
  175.         return $this->redirect($this->generateUrl('helpdesk_member_ticket', ['ticketId' => $ticket->getId()]));
  176.     }
  177.     public function updateThreadXHR(Request $request)
  178.     {
  179.         $json = [];
  180.         $em $this->getDoctrine()->getManager();
  181.         $content json_decode($request->getContent(), true);
  182.         $thread $em->getRepository(TicketThread::class)->find($request->attributes->get('threadId'));
  183.         $ticket $thread->getTicket();
  184.         $user $this->userService->getSessionUser();
  185.         // Proceed only if user has access to the resource
  186.         if ( (!$this->userService->getSessionUser()) || (false == $this->ticketService->isTicketAccessGranted($ticket$user)) ) 
  187.         {
  188.             throw new \Exception('Access Denied'403);
  189.         }
  190.         if ($request->getMethod() == "PUT") {
  191.             // $this->isAuthorized('ROLE_AGENT_EDIT_THREAD_NOTE');
  192.             if (str_replace(' ','',str_replace('&nbsp;','',trim(strip_tags($content['reply'], '<img>')))) != "") {
  193.                 $thread->setMessage($content['reply']);
  194.                 $em->persist($thread);
  195.                 $em->flush();
  196.                 $ticket->currentThread $thread;
  197.                 // Trigger agent reply event
  198.                 $event = new CoreWorkflowEvents\Ticket\ThreadUpdate();
  199.                 $event
  200.                     ->setTicket($ticket)
  201.                 ;
  202.                 $this->eventDispatcher->dispatch($event'uvdesk.automation.workflow.execute');
  203.                 $json['alertMessage'] = $this->translator->trans('Success ! Thread updated successfully.');
  204.                 $json['alertClass'] = 'success';
  205.             } else {
  206.                 $json['alertMessage'] = $this->translator->trans('Error ! Reply field can not be blank.');
  207.                 $json['alertClass'] = 'error';
  208.             }
  209.         }
  210.         return new Response(json_encode($json), 200, ['Content-Type' => 'application/json']);
  211.     }
  212.     public function threadXHR(Request $request)
  213.     {
  214.         $json = array();
  215.         $content json_decode($request->getContent(), true);
  216.         $em $this->getDoctrine()->getManager();
  217.         $ticket $em->getRepository(Ticket::class)->findOneById($content['ticketId']); 
  218.         // Proceed only if user has access to the resource
  219.         if (false == $this->ticketService->isTicketAccessGranted($ticket)){
  220.             throw new \Exception('Access Denied'403);
  221.         }
  222.         
  223.         $threadId $request->attributes->get('threadId');
  224.         if ($request->getMethod() == "DELETE") {
  225.             $thread $em->getRepository(TicketThread::class)->findOneBy(array('id' => $threadId'ticket' => $content['ticketId']));
  226.             $projectDir $this->kernel->getProjectDir();
  227.             
  228.             if ($thread) {
  229.                 $this->fileUploadService->fileRemoveFromFolder($projectDir."/public/assets/threads/".$threadId);
  230.                 
  231.                 // Trigger thread deleted event
  232.                 // $event = new CoreWorkflowEvents\Ticket\ThreadDeleted();
  233.                 // $event
  234.                 //     ->setTicket($ticket)
  235.                 // ;
  236.                 // 
  237.                 // $this->eventDispatcher->dispatch($event, 'uvdesk.automation.workflow.execute');
  238.                 $em->remove($thread);
  239.                 $em->flush();
  240.                 $json['alertClass'] = 'success';
  241.                 $json['alertMessage'] = $this->translator->trans('Success ! Thread removed successfully.');
  242.             } else {
  243.                 $json['alertClass'] = 'danger';
  244.                 $json['alertMessage'] = $this->translator->trans('Error ! Invalid thread.');
  245.             }
  246.         } elseif ($request->getMethod() == "PATCH") {
  247.             $thread $em->getRepository(TicketThread::class)->findOneBy(array('id' => $request->attributes->get('threadId'), 'ticket' => $content['ticketId']));
  248.             if ($thread) {
  249.                 if ($content['updateType'] == 'lock') {
  250.                     $thread->setIsLocked($content['isLocked']);
  251.                     $em->persist($thread);
  252.                     $em->flush();
  253.                     $json['alertMessage'] = $this->translator->trans($content['isLocked'] ? 'Success ! Thread locked successfully.' 'Success ! Thread unlocked successfully.');
  254.                     $json['alertClass'] = 'success';
  255.                 } elseif ($content['updateType'] == 'bookmark') {
  256.                     $thread->setIsBookmarked($content['bookmark']);
  257.                     $em->persist($thread);
  258.                     $em->flush();
  259.                     $json['alertMessage'] = $this->translator->trans($content['bookmark'] ? 'Success ! Thread pinned successfully.' 'Success ! unpinned removed successfully.');
  260.                     $json['alertClass'] = 'success';
  261.                 }
  262.             } else {
  263.                 $json['alertClass'] = 'danger';
  264.                 $json['alertMessage'] = $this->translator->trans('Error ! Invalid thread.');
  265.             }
  266.         }
  267.         return new Response(json_encode($json), 200, ['Content-Type' => 'application/json']);
  268.     }
  269. }