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 $userService, TranslatorInterface $translator, TicketService $ticketService, EmailService $emailService, EventDispatcherInterface $eventDispatcher, KernelInterface $kernel, FileUploadService $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($ticketId, Request $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. // $adminReply = str_replace(['<p>','</p>'],"",$params['reply']);
  73. $threadDetails = [
  74. 'user' => $this->getUser(),
  75. 'createdBy' => 'agent',
  76. 'source' => 'website',
  77. 'threadType' => strtolower($params['threadType']),
  78. 'message' => str_replace(['&lt;script&gt;', '&lt;/script&gt;'], '', htmlspecialchars($params['reply'])),
  79. 'attachments' => $request->files->get('attachments')
  80. ];
  81. if (! empty($params['status'])) {
  82. $ticketStatus = $entityManager->getRepository(TicketStatus::class)->findOneByCode($params['status']);
  83. $ticket->setStatus($ticketStatus);
  84. $event = new CoreWorkflowEvents\Ticket\Status();
  85. $event
  86. ->setTicket($ticket);
  87. $this->eventDispatcher->dispatch($event, 'uvdesk.automation.workflow.execute');
  88. }
  89. if (isset($params['to'])) {
  90. $threadDetails['to'] = $params['to'];
  91. }
  92. if (isset($params['cc'])) {
  93. $threadDetails['cc'] = $params['cc'];
  94. }
  95. if (isset($params['cccol'])) {
  96. $threadDetails['cccol'] = $params['cccol'];
  97. }
  98. if (isset($params['bcc'])) {
  99. $threadDetails['bcc'] = $params['bcc'];
  100. }
  101. // Create Thread
  102. $thread = $this->ticketService->createThread($ticket, $threadDetails);
  103. // $this->addFlash('success', ucwords($params['threadType']) . " added successfully.");
  104. // @TODO: Remove Agent Draft Thread
  105. // @TODO: Trigger Thread Created Event
  106. // @TODO: Cross Review
  107. // check for thread types
  108. switch ($thread->getThreadType()) {
  109. case 'note':
  110. $event = new CoreWorkflowEvents\Ticket\Note();
  111. $event
  112. ->setTicket($ticket)
  113. ->setThread($thread)
  114. ;
  115. $this->eventDispatcher->dispatch($event, 'uvdesk.automation.workflow.execute');
  116. // @TODO: Render response on the basis of event response (if propagation was stopped or not)
  117. $this->addFlash('success', $this->translator->trans('Note added to ticket successfully.'));
  118. break;
  119. case 'reply':
  120. if ($ticket->getIsTrashed() == false) {
  121. $event = new CoreWorkflowEvents\Ticket\AgentReply();
  122. $event
  123. ->setTicket($ticket)
  124. ->setThread($thread)
  125. ;
  126. $this->eventDispatcher->dispatch($event, 'uvdesk.automation.workflow.execute');
  127. $this->eventDispatcher->dispatch($event, 'uvdesk.automation.report_app.workflow.execute');
  128. }
  129. // @TODO: Render response on the basis of event response (if propagation was stopped or not)
  130. $this->addFlash('success', $this->translator->trans('Success ! Reply added successfully.'));
  131. break;
  132. case 'forward':
  133. // Prepare headers
  134. $headers = ['References' => $ticket->getReferenceIds()];
  135. if (null != $ticket->currentThread->getMessageId()) {
  136. $headers['In-Reply-To'] = $ticket->currentThread->getMessageId();
  137. }
  138. // Prepare attachments
  139. $attachments = $entityManager->getRepository(Attachment::class)->findByThread($thread);
  140. $projectDir = $this->kernel->getProjectDir();
  141. $attachments = array_map(function($attachment) use ($projectDir) {
  142. return str_replace('//', '/', $projectDir . "/public" . $attachment->getPath());
  143. }, $attachments);
  144. $message = '<html><body style="background-image: none"><p>'.html_entity_decode($thread->getMessage()).'</p></body></html>';
  145. // Forward thread to users
  146. try {
  147. $messageId = $this->emailService->sendMail($params['subject'] ?? ("Forward: " . $ticket->getSubject()), $message, $thread->getReplyTo(), $headers, $ticket->getMailboxEmail(), $attachments ?? [], $thread->getCc() ?: [], $thread->getBcc() ?: []);
  148. if (! empty($messageId)) {
  149. $thread->setMessageId($messageId);
  150. $entityManager->persist($thread);
  151. $entityManager->flush();
  152. }
  153. } catch (\Exception $e) {
  154. // Do nothing ...
  155. // @TODO: Log exception
  156. }
  157. // @TODO: Render response on the basis of event response (if propagation was stopped or not)
  158. $this->addFlash('success', $this->translator->trans('Reply added to the ticket and forwarded successfully.'));
  159. break;
  160. default:
  161. break;
  162. }
  163. // Redirect to either Ticket View | Ticket Listings
  164. if ('redirect' === $params['nextView']) {
  165. return $this->redirect($this->generateUrl('helpdesk_member_ticket_collection'));
  166. }
  167. return $this->redirect($this->generateUrl('helpdesk_member_ticket', ['ticketId' => $ticket->getId()]));
  168. }
  169. public function updateThreadXHR(Request $request)
  170. {
  171. $json = [];
  172. $em = $this->getDoctrine()->getManager();
  173. $content = json_decode($request->getContent(), true);
  174. $thread = $em->getRepository(TicketThread::class)->find($request->attributes->get('threadId'));
  175. $ticket = $thread->getTicket();
  176. $user = $this->userService->getSessionUser();
  177. // Proceed only if user has access to the resource
  178. if (
  179. (!$this->userService->getSessionUser())
  180. || (false == $this->ticketService->isTicketAccessGranted($ticket, $user))
  181. ) {
  182. throw new \Exception('Access Denied', 403);
  183. }
  184. if ($request->getMethod() == "PUT") {
  185. // $this->isAuthorized('ROLE_AGENT_EDIT_THREAD_NOTE');
  186. if (str_replace(' ','',str_replace('&nbsp;','',trim(strip_tags($content['reply'], '<img>')))) != "") {
  187. $thread->setMessage($content['reply']);
  188. $em->persist($thread);
  189. $em->flush();
  190. $ticket->currentThread = $thread;
  191. // Trigger agent reply event
  192. $event = new CoreWorkflowEvents\Ticket\ThreadUpdate();
  193. $event
  194. ->setTicket($ticket)
  195. ;
  196. $this->eventDispatcher->dispatch($event, 'uvdesk.automation.workflow.execute');
  197. $json['alertMessage'] = $this->translator->trans('Success ! Thread updated successfully.');
  198. $json['alertClass'] = 'success';
  199. } else {
  200. $json['alertMessage'] = $this->translator->trans('Error ! Reply field can not be blank.');
  201. $json['alertClass'] = 'error';
  202. }
  203. }
  204. return new Response(json_encode($json), 200, ['Content-Type' => 'application/json']);
  205. }
  206. public function threadXHR(Request $request)
  207. {
  208. $json = array();
  209. $content = json_decode($request->getContent(), true);
  210. $em = $this->getDoctrine()->getManager();
  211. $ticket = $em->getRepository(Ticket::class)->findOneById($content['ticketId']);
  212. // Proceed only if user has access to the resource
  213. if (false == $this->ticketService->isTicketAccessGranted($ticket)){
  214. throw new \Exception('Access Denied', 403);
  215. }
  216. $threadId = $request->attributes->get('threadId');
  217. if ($request->getMethod() == "DELETE") {
  218. $thread = $em->getRepository(TicketThread::class)->findOneBy(array('id' => $threadId, 'ticket' => $content['ticketId']));
  219. $projectDir = $this->kernel->getProjectDir();
  220. if ($thread) {
  221. $this->fileUploadService->fileRemoveFromFolder($projectDir."/public/assets/threads/".$threadId);
  222. // Trigger thread deleted event
  223. // $event = new CoreWorkflowEvents\Ticket\ThreadDeleted();
  224. // $event
  225. // ->setTicket($ticket)
  226. // ;
  227. //
  228. // $this->eventDispatcher->dispatch($event, 'uvdesk.automation.workflow.execute');
  229. $em->remove($thread);
  230. $em->flush();
  231. $json['alertClass'] = 'success';
  232. $json['alertMessage'] = $this->translator->trans('Success ! Thread removed successfully.');
  233. } else {
  234. $json['alertClass'] = 'danger';
  235. $json['alertMessage'] = $this->translator->trans('Error ! Invalid thread.');
  236. }
  237. } elseif ($request->getMethod() == "PATCH") {
  238. $thread = $em->getRepository(TicketThread::class)->findOneBy(array('id' => $request->attributes->get('threadId'), 'ticket' => $content['ticketId']));
  239. if ($thread) {
  240. if ($content['updateType'] == 'lock') {
  241. $thread->setIsLocked($content['isLocked']);
  242. $em->persist($thread);
  243. $em->flush();
  244. $json['alertMessage'] = $this->translator->trans($content['isLocked'] ? 'Success ! Thread locked successfully.' : 'Success ! Thread unlocked successfully.');
  245. $json['alertClass'] = 'success';
  246. } elseif ($content['updateType'] == 'bookmark') {
  247. $thread->setIsBookmarked($content['bookmark']);
  248. $em->persist($thread);
  249. $em->flush();
  250. $json['alertMessage'] = $this->translator->trans($content['bookmark'] ? 'Success ! Thread pinned successfully.' : 'Success ! unpinned removed successfully.');
  251. $json['alertClass'] = 'success';
  252. }
  253. } else {
  254. $json['alertClass'] = 'danger';
  255. $json['alertMessage'] = $this->translator->trans('Error ! Invalid thread.');
  256. }
  257. }
  258. return new Response(json_encode($json), 200, ['Content-Type' => 'application/json']);
  259. }
  260. }