vendor/uvdesk/support-center-bundle/Controller/Ticket.php line 74

Open in your IDE?
  1. <?php
  2. namespace Webkul\UVDesk\SupportCenterBundle\Controller;
  3. use Doctrine\ORM\EntityManagerInterface;
  4. use Symfony\Component\HttpFoundation\Request;
  5. use Symfony\Component\HttpFoundation\Response;
  6. use Symfony\Component\HttpKernel\KernelInterface;
  7. use Symfony\Component\HttpFoundation\StreamedResponse;
  8. use Symfony\Contracts\Translation\TranslatorInterface;
  9. use Symfony\Component\HttpFoundation\ResponseHeaderBag;
  10. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  11. use Symfony\Component\DependencyInjection\ContainerInterface;
  12. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  13. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  14. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  15. use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
  16. use Webkul\UVDesk\CoreFrameworkBundle\Entity as CoreEntities;
  17. use Webkul\UVDesk\CoreFrameworkBundle\Services as CoreServices;
  18. use Webkul\UVDesk\SupportCenterBundle\Entity as SupportEntities;
  19. use Webkul\UVDesk\SupportCenterBundle\Form\Ticket as TicketForm;
  20. use Webkul\UVDesk\CoreFrameworkBundle\Workflow\Events as CoreWorkflowEvents;
  21. class Ticket extends AbstractController
  22. {
  23. private $userService;
  24. private $eventDispatcher;
  25. private $translator;
  26. private $uvdeskService;
  27. private $ticketService;
  28. private $recaptchaService;
  29. private $kernel;
  30. private $em;
  31. public function __construct(CoreServices\UserService $userService, CoreServices\UVDeskService $uvdeskService, EventDispatcherInterface $eventDispatcher, TranslatorInterface $translator, CoreServices\TicketService $ticketService, CoreServices\ReCaptchaService $recaptchaService, KernelInterface $kernel, EntityManagerInterface $entityManager)
  32. {
  33. $this->userService = $userService;
  34. $this->eventDispatcher = $eventDispatcher;
  35. $this->translator = $translator;
  36. $this->uvdeskService = $uvdeskService;
  37. $this->ticketService = $ticketService;
  38. $this->recaptchaService = $recaptchaService;
  39. $this->kernel = $kernel;
  40. $this->em = $entityManager;
  41. }
  42. protected function isWebsiteActive()
  43. {
  44. $website = $this->em->getRepository(CoreEntities\Website::class)->findOneByCode('knowledgebase');
  45. if (! empty($website)) {
  46. $knowledgebaseWebsite = $this->em->getRepository(SupportEntities\KnowledgebaseWebsite::class)->findOneBy(['website' => $website->getId(), 'status' => 1]);
  47. if (
  48. ! empty($knowledgebaseWebsite)
  49. && true == $knowledgebaseWebsite->getIsActive()
  50. ) {
  51. return true;
  52. }
  53. }
  54. $this->noResultFound();
  55. }
  56. /**
  57. * If customer is playing with url and no result is found then what will happen
  58. * @return
  59. */
  60. protected function noResultFound()
  61. {
  62. throw new NotFoundHttpException('Not found !');
  63. }
  64. public function ticketadd(Request $request, ContainerInterface $container)
  65. {
  66. $this->isWebsiteActive();
  67. $formErrors = $errors = array();
  68. $website = $this->em->getRepository(CoreEntities\Website::class)->findOneByCode('knowledgebase');
  69. $websiteConfiguration = $this->uvdeskService->getActiveConfiguration($website->getId());
  70. if (
  71. ! $websiteConfiguration
  72. || ! $websiteConfiguration->getTicketCreateOption()
  73. || ($websiteConfiguration->getLoginRequiredToCreate()
  74. && ! $this->getUser())
  75. ) {
  76. return $this->redirect($this->generateUrl('helpdesk_knowledgebase'));
  77. }
  78. $post = $request->request->all();
  79. $recaptchaDetails = $this->recaptchaService->getRecaptchaDetails();
  80. if ($request->getMethod() == "POST") {
  81. if (
  82. $recaptchaDetails
  83. && $recaptchaDetails->getIsActive() == true
  84. && $this->recaptchaService->getReCaptchaResponse($request->request->get('g-recaptcha-response'))
  85. ) {
  86. $this->addFlash('warning', $this->translator->trans("Warning ! Please select correct CAPTCHA !"));
  87. } else {
  88. if ($_POST) {
  89. $error = false;
  90. try {
  91. $customFieldsService = null;
  92. if ($this->userService->isFileExists('apps/uvdesk/custom-fields')) {
  93. $customFieldsService = $this->get('uvdesk_package_custom_fields.service');
  94. } else if ($this->userService->isFileExists('apps/uvdesk/form-component')) {
  95. $customFieldsService = $this->get('uvdesk_package_form_component.service');
  96. }
  97. if (! empty($customFieldsService)) {
  98. if (
  99. $request->files->get('customFields')
  100. && ! $customFieldsService->validateAttachmentsSize($request->files->get('customFields'))
  101. ) {
  102. $error = true;
  103. $this->addFlash(
  104. 'warning',
  105. $this->translator->trans("Warning ! Files size can not exceed %size% MB", [
  106. '%size%' => $this->getParameter('max_upload_size')
  107. ])
  108. );
  109. }
  110. }
  111. } catch (\Exception $e) {
  112. // @TODO: Log execption message
  113. }
  114. $ticket = new CoreEntities\Ticket();
  115. $loggedUser = $this->get('security.token_storage')->getToken()->getUser();
  116. if (
  117. ! empty($loggedUser)
  118. && $loggedUser != 'anon.'
  119. ) {
  120. $form = $this->createForm(TicketForm::class, $ticket, [
  121. 'container' => $container,
  122. 'entity_manager' => $this->em,
  123. ]);
  124. $email = $loggedUser->getEmail();
  125. try {
  126. $name = $loggedUser->getFirstName() . ' ' . $loggedUser->getLastName();
  127. } catch (\Exception $e) {
  128. $name = explode(' ', strstr($email, '@', true));
  129. }
  130. } else {
  131. $form = $this->createForm(TicketForm::class, $ticket, [
  132. 'container' => $container,
  133. 'entity_manager' => $this->em,
  134. ]);
  135. $email = $request->request->get('from');
  136. $name = explode(' ', $request->request->get('name'));
  137. }
  138. $website = $this->em->getRepository(CoreEntities\Website::class)->findOneByCode('knowledgebase');
  139. if (
  140. ! empty($email)
  141. && $this->ticketService->isEmailBlocked($email, $website)
  142. ) {
  143. $request->getSession()->getFlashBag()->set('warning', $this->translator->trans('Warning ! Cannot create ticket, given email is blocked by admin.'));
  144. return $this->redirect($this->generateUrl('helpdesk_customer_create_ticket'));
  145. }
  146. if ($request->request->all())
  147. $form->submit($request->request->all());
  148. if ($form->isValid() && !count($formErrors) && !$error) {
  149. $data = array(
  150. 'from' => $email,
  151. 'subject' => $request->request->get('subject'),
  152. 'reply' => str_replace(['&lt;script&gt;', '&lt;/script&gt;'], '', htmlspecialchars($request->request->get('reply'))),
  153. 'firstName' => $name[0],
  154. 'lastName' => isset($name[1]) ? $name[1] : '',
  155. 'role' => 4,
  156. 'active' => true
  157. );
  158. $data['type'] = $this->em->getRepository(CoreEntities\TicketType::class)->find($request->request->get('type'));
  159. if (! is_object($data['customer'] = $this->container->get('security.token_storage')->getToken()->getUser()) == "anon.") {
  160. $supportRole = $this->em->getRepository(CoreEntities\SupportRole::class)->findOneByCode("ROLE_CUSTOMER");
  161. $customerEmail = $params['email'] = $request->request->get('from');
  162. $customer = $this->em->getRepository(CoreEntities\User::class)->findOneBy(array('email' => $customerEmail));
  163. $params['flag'] = (!$customer) ? 1 : 0;
  164. $data['firstName'] = current($nameDetails = explode(' ', $request->request->get('name')));
  165. $data['fullname'] = $request->request->get('name');
  166. $data['lastName'] = ($data['firstName'] != end($nameDetails)) ? end($nameDetails) : " ";
  167. $data['from'] = $customerEmail;
  168. $data['role'] = 4;
  169. $data['customer'] = $this->userService->createUserInstance($customerEmail, $data['fullname'], $supportRole, $extras = ["active" => true]);
  170. } else {
  171. $userDetail = $this->em->getRepository(CoreEntities\User::class)->find($data['customer']->getId());
  172. $data['email'] = $customerEmail = $data['customer']->getEmail();
  173. $nameCollection = [$userDetail->getFirstName(), $userDetail->getLastName()];
  174. $name = implode(' ', $nameCollection);
  175. $data['fullname'] = $name;
  176. }
  177. $data['user'] = $data['customer'];
  178. $data['subject'] = $request->request->get('subject');
  179. $data['source'] = 'website';
  180. $data['threadType'] = 'create';
  181. $data['message'] = $data['reply'];
  182. $data['createdBy'] = 'customer';
  183. $data['attachments'] = $request->files->get('attachments');
  184. if (! empty($request->server->get("HTTP_CF_CONNECTING_IP"))) {
  185. $data['ipAddress'] = $request->server->get("HTTP_CF_CONNECTING_IP");
  186. if (!empty($request->server->get("HTTP_CF_IPCOUNTRY"))) {
  187. $data['ipAddress'] .= '(' . $request->server->get("HTTP_CF_IPCOUNTRY") . ')';
  188. }
  189. }
  190. $thread = $this->ticketService->createTicketBase($data);
  191. if (! empty($thread)) {
  192. $ticket = $thread->getTicket();
  193. if (
  194. $request->request->get('customFields')
  195. || $request->files->get('customFields')
  196. ) {
  197. $this->ticketService->addTicketCustomFields($thread, $request->request->get('customFields'), $request->files->get('customFields'));
  198. }
  199. $this->addFlash('success', $this->translator->trans('Success ! Ticket has been created successfully.'));
  200. } else {
  201. $this->addFlash('warning', $this->translator->trans('Warning ! Can not create ticket, invalid details.'));
  202. }
  203. // Trigger ticket created event
  204. $event = new CoreWorkflowEvents\Ticket\Create();
  205. $event
  206. ->setTicket($thread->getTicket());
  207. $this->eventDispatcher->dispatch($event, 'uvdesk.automation.workflow.execute');
  208. return null != $this->getUser() ? $this->redirect($this->generateUrl('helpdesk_customer_ticket_collection')) : $this->redirect($this->generateUrl('helpdesk_knowledgebase'));
  209. } else {
  210. $errors = $this->getFormErrors($form);
  211. $errors = array_merge($errors, $formErrors);
  212. }
  213. } else {
  214. $this->addFlash(
  215. 'warning',
  216. $this->translator->trans("Warning ! Post size can not exceed 25MB")
  217. );
  218. }
  219. if (
  220. isset($errors)
  221. && count($errors)
  222. ) {
  223. $this->addFlash('warning', key($errors) . ': ' . reset($errors));
  224. }
  225. }
  226. }
  227. $breadcrumbs = [
  228. [
  229. 'label' => $this->translator->trans('Support Center'),
  230. 'url' => $this->generateUrl('helpdesk_knowledgebase')
  231. ],
  232. [
  233. 'label' => $this->translator->trans("Create Ticket Request"),
  234. 'url' => '#'
  235. ],
  236. ];
  237. return $this->render(
  238. '@UVDeskSupportCenter/Knowledgebase/ticket.html.twig',
  239. array(
  240. 'formErrors' => $formErrors,
  241. 'errors' => json_encode($errors),
  242. 'customFieldsValues' => $request->request->get('customFields'),
  243. 'breadcrumbs' => $breadcrumbs,
  244. 'post' => $post
  245. )
  246. );
  247. }
  248. public function saveReply(int $id, Request $request)
  249. {
  250. $this->isWebsiteActive();
  251. $data = $request->request->all();
  252. $ticket = $this->em->getRepository(CoreEntities\Ticket::class)->find($id);
  253. $user = $this->userService->getSessionUser();
  254. // process only if access for the resource.
  255. if (empty($ticket) || ((!empty($user)) && $user->getId() != $ticket->getCustomer()->getId())) {
  256. if (! $this->isCollaborator($ticket, $user)) {
  257. throw new \Exception('Access Denied', 403);
  258. }
  259. }
  260. if ($_POST) {
  261. if (str_replace(' ', '', str_replace('&nbsp;', '', trim(strip_tags($data['message'], '<img>')))) != "") {
  262. if (!$ticket)
  263. $this->noResultFound();
  264. $data['ticket'] = $ticket;
  265. $data['user'] = $this->userService->getCurrentUser();
  266. // Checking if reply is from collaborator end
  267. $isTicketCollaborator = $ticket->getCollaborators() ? $ticket->getCollaborators()->toArray() : [];
  268. $isCollaborator = false;
  269. foreach ($isTicketCollaborator as $value) {
  270. if ($value->getId() == $data['user']->getId()) {
  271. $isCollaborator = true;
  272. }
  273. }
  274. // @TODO: Refactor -> Why are we filtering only these two characters?
  275. $data['message'] = str_replace(['&lt;script&gt;', '&lt;/script&gt;'], '', htmlspecialchars($data['message']));
  276. $userDetail = $this->userService->getCustomerPartialDetailById($data['user']->getId());
  277. $data['fullname'] = $userDetail['name'];
  278. $data['source'] = 'website';
  279. $data['createdBy'] = $isCollaborator ? 'collaborator' : 'customer';
  280. $data['attachments'] = $request->files->get('attachments');
  281. $thread = $this->ticketService->createThread($ticket, $data);
  282. $status = $this->em->getRepository(CoreEntities\TicketStatus::class)->findOneByCode($data['status']);
  283. if ($status) {
  284. $ticket
  285. ->setStatus($status);
  286. $this->em->persist($ticket);
  287. }
  288. $ticket->setCustomerRepliedAt(new \DateTime('now'));
  289. $this->em->persist($ticket);
  290. $this->em->flush();
  291. if ($thread->getcreatedBy() == 'customer') {
  292. $event = new CoreWorkflowEvents\Ticket\CustomerReply();
  293. $event
  294. ->setTicket($ticket)
  295. ->setThread($thread)
  296. ;
  297. } else {
  298. $event = new CoreWorkflowEvents\Ticket\CollaboratorReply();
  299. $event
  300. ->setTicket($ticket)
  301. ->setThread($thread)
  302. ;
  303. }
  304. $this->eventDispatcher->dispatch($event, 'uvdesk.automation.workflow.execute');
  305. $this->eventDispatcher->dispatch($event, 'uvdesk.automation.report_app.workflow.execute');
  306. $this->addFlash('success', $this->translator->trans('Success ! Reply added successfully.'));
  307. } else {
  308. $this->addFlash('warning', $this->translator->trans('Warning ! Reply field can not be blank.'));
  309. }
  310. } else {
  311. $this->addFlash('warning', $this->translator->trans('Warning ! Post size can not exceed 25MB'));
  312. }
  313. return $this->redirect($this->generateUrl('helpdesk_customer_ticket', array(
  314. 'id' => $ticket->getId()
  315. )));
  316. }
  317. public function tickets(Request $request)
  318. {
  319. $this->isWebsiteActive();
  320. // List Announcement if any
  321. $announcements = $this->em->getRepository(SupportEntities\Announcement::class)->findBy(['isActive' => 1]);
  322. $groupAnnouncement = [];
  323. foreach ($announcements as $announcement) {
  324. $announcementGroupId = $announcement->getGroup();
  325. $isTicketExist = $this->em->getRepository(CoreEntities\Ticket::class)->findBy(['supportGroup' => $announcementGroupId, 'customer' => $this->userService->getCurrentUser()]);
  326. if (! empty($isTicketExist)) {
  327. $groupAnnouncement[] = $announcement;
  328. }
  329. }
  330. return $this->render(
  331. '@UVDeskSupportCenter/Knowledgebase/ticketList.html.twig',
  332. array(
  333. 'searchDisable' => true,
  334. 'groupAnnouncement' => $groupAnnouncement
  335. )
  336. );
  337. }
  338. /**
  339. * ticketListXhrAction "Filter and sort ticket collection on ajax request"
  340. * @param Object $request "HTTP Request object"
  341. * @return JSON "JSON response"
  342. */
  343. public function ticketListXhr(Request $request, ContainerInterface $container)
  344. {
  345. $this->isWebsiteActive();
  346. $json = array();
  347. if ($request->isXmlHttpRequest()) {
  348. $repository = $this->em->getRepository(CoreEntities\Ticket::class);
  349. $json = $repository->getAllCustomerTickets($request->query, $container);
  350. }
  351. $response = new Response(json_encode($json));
  352. $response->headers->set('Content-Type', 'application/json');
  353. return $response;
  354. }
  355. /**
  356. * threadListXhrAction "Filter and sort user collection on ajx request"
  357. * @param Object $request "HTTP Request object"
  358. * @return JSON "JSON response"
  359. */
  360. public function threadListXhr(Request $request, ContainerInterface $container)
  361. {
  362. $this->isWebsiteActive();
  363. $json = array();
  364. if ($request->isXmlHttpRequest()) {
  365. $repository = $this->em->getRepository(CoreEntities\Thread::class);
  366. $json = $repository->getAllCustomerThreads($request->attributes->get('id'), $request->query, $container);
  367. }
  368. $response = new Response(json_encode($json));
  369. $response->headers->set('Content-Type', 'application/json');
  370. return $response;
  371. }
  372. public function ticketView($id, Request $request)
  373. {
  374. $this->isWebsiteActive();
  375. $user = $this->userService->getSessionUser();
  376. $ticket = $this->em->getRepository(CoreEntities\Ticket::class)->findOneBy(['id' => $id]);
  377. $isConfirmColl = false;
  378. if ($ticket->getIsTrashed()) {
  379. return $this->redirect($this->generateUrl('helpdesk_customer_ticket_collection'));
  380. }
  381. if ($ticket == null && empty($ticket)) {
  382. throw new NotFoundHttpException('Page Not Found!');
  383. }
  384. if (
  385. ! empty($ticket)
  386. && ((!empty($user))
  387. && $user->getId() != $ticket->getCustomer()->getId())
  388. ) {
  389. if ($this->isCollaborator($ticket, $user)) {
  390. $isConfirmColl = true;
  391. }
  392. if ($isConfirmColl != true) {
  393. throw new \Exception('Access Denied', 403);
  394. }
  395. }
  396. if (
  397. ! empty($user)
  398. && $user->getId() == $ticket->getCustomer()->getId()
  399. ) {
  400. $ticket->setIsCustomerViewed(1);
  401. $this->em->persist($ticket);
  402. $this->em->flush();
  403. }
  404. $checkTicket = $this->em->getRepository(CoreEntities\Ticket::class)->isTicketCollaborator($ticket, $user->getEmail());
  405. $twigResponse = [
  406. 'ticket' => $ticket,
  407. 'searchDisable' => true,
  408. 'initialThread' => $this->ticketService->getTicketInitialThreadDetails($ticket),
  409. 'localizedCreateAtTime' => $this->userService->getLocalizedFormattedTime($ticket->getCreatedAt(), $user),
  410. 'isCollaborator' => $checkTicket,
  411. ];
  412. return $this->render('@UVDeskSupportCenter/Knowledgebase/ticketView.html.twig', $twigResponse);
  413. }
  414. // Check if user is collaborator for the ticket
  415. public function isCollaborator($ticket, $user)
  416. {
  417. $isCollaborator = false;
  418. if (! empty($ticket->getCollaborators()->toArray())) {
  419. foreach ($ticket->getCollaborators()->toArray() as $collaborator) {
  420. if ($collaborator->getId() == $user->getId()) {
  421. $isCollaborator = true;
  422. }
  423. }
  424. }
  425. return $isCollaborator;
  426. }
  427. // Ticket rating
  428. public function rateTicket(Request $request)
  429. {
  430. $this->isWebsiteActive();
  431. $json = array();
  432. $data = json_decode($request->getContent(), true);
  433. $id = $data['id'];
  434. $count = intval($data['rating']);
  435. if ($count > 0 || $count < 6) {
  436. $customer = $this->userService->getCurrentUser();
  437. $ticket = $this->em->getRepository(CoreEntities\Ticket::class)->findOneBy([
  438. 'id' => $id,
  439. 'customer' => $customer
  440. ]);
  441. if (empty($ticket)) {
  442. $json['alertClass'] = 'danger';
  443. $json['alertMessage'] = $this->translator->trans('Warning ! Invalid rating.');
  444. $response = new Response(json_encode($json));
  445. $response->headers->set('Content-Type', 'application/json');
  446. return $response;
  447. }
  448. $rating = $this->em->getRepository(CoreEntities\TicketRating::class)->findOneBy(array('ticket' => $id, 'customer' => $customer->getId()));
  449. if ($rating) {
  450. $rating->setcreatedAt(new \DateTime);
  451. $rating->setStars($count);
  452. $this->em->persist($rating);
  453. $this->em->flush();
  454. } else {
  455. $rating = new CoreEntities\TicketRating();
  456. $rating->setStars($count);
  457. $rating->setCustomer($customer);
  458. $rating->setTicket($ticket);
  459. $this->em->persist($rating);
  460. $this->em->flush();
  461. }
  462. $json['alertClass'] = 'success';
  463. $json['alertMessage'] = $this->translator->trans('Success ! Rating has been successfully added.');
  464. } else {
  465. $json['alertClass'] = 'danger';
  466. $json['alertMessage'] = $this->translator->trans('Warning ! Invalid rating.');
  467. }
  468. $response = new Response(json_encode($json));
  469. $response->headers->set('Content-Type', 'application/json');
  470. return $response;
  471. }
  472. public function downloadAttachmentZip(Request $request)
  473. {
  474. $threadId = $request->attributes->get('threadId');
  475. $attachmentRepository = $this->em->getRepository(CoreEntities\Attachment::class);
  476. $threadRepository = $this->em->getRepository(CoreEntities\Thread::class);
  477. // Get thread and attachments
  478. $thread = $threadRepository->findOneById($threadId);
  479. $attachments = $attachmentRepository->findByThread($threadId);
  480. if (!$attachments) {
  481. $this->noResultFound();
  482. }
  483. // Access control
  484. $ticket = $thread->getTicket();
  485. $user = $this->userService->getSessionUser();
  486. if (
  487. empty($ticket)
  488. || (!empty($user) && $user->getId() != $ticket->getCustomer()->getId())
  489. ) {
  490. if (!$this->isCollaborator($ticket, $user)) {
  491. throw new \Exception('Access Denied', 403);
  492. }
  493. }
  494. // Create ZIP file with proper path
  495. $zipName = sys_get_temp_dir() . '/attachments_' . $threadId . '.zip';
  496. // Make sure directory exists
  497. $zipDir = dirname($zipName);
  498. if (! file_exists($zipDir)) {
  499. mkdir($zipDir, 0777, true);
  500. }
  501. // Create new ZIP archive
  502. $zip = new \ZipArchive();
  503. if ($zip->open($zipName, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== TRUE) {
  504. throw new \Exception("Cannot create zip file");
  505. }
  506. // Add files to ZIP
  507. foreach ($attachments as $attachment) {
  508. $filePath = substr($attachment->getPath(), 1); // Remove leading slash
  509. if (file_exists($filePath)) {
  510. $zip->addFile($filePath, basename($filePath));
  511. }
  512. }
  513. $zip->close();
  514. // Check if file was created successfully
  515. if (!file_exists($zipName)) {
  516. throw new \Exception("ZIP file could not be created");
  517. }
  518. // Stream the file to prevent memory issues
  519. $response = new BinaryFileResponse($zipName);
  520. $response->headers->set('Content-Type', 'application/zip');
  521. $response->headers->set('Content-Disposition', ResponseHeaderBag::DISPOSITION_ATTACHMENT . '; filename="attachments_' . $threadId . '.zip"');
  522. $response->deleteFileAfterSend(true); // Clean up after sending
  523. return $response;
  524. }
  525. public function downloadAttachment(Request $request)
  526. {
  527. $attachmentId = $request->attributes->get('attachmentId');
  528. $attachment = $this->em->getRepository(CoreEntities\Attachment::class)->findOneById($attachmentId);
  529. if (empty($attachment)) {
  530. $this->noResultFound();
  531. }
  532. $thread = $attachment->getThread();
  533. if (! empty($thread)) {
  534. $ticket = $thread->getTicket();
  535. $user = $this->userService->getSessionUser();
  536. // process only if access for the resource.
  537. if (
  538. empty($ticket)
  539. || ((! empty($user)) && $user->getId() != $ticket->getCustomer()->getId())
  540. ) {
  541. if (! $this->isCollaborator($ticket, $user)) {
  542. throw new \Exception('Access Denied', 403);
  543. }
  544. }
  545. }
  546. $path = $this->kernel->getProjectDir() . "/public/" . $attachment->getPath();
  547. return new StreamedResponse(function () use ($path) {
  548. readfile($path);
  549. }, 200, [
  550. 'Content-Type' => $attachment->getContentType(),
  551. 'Content-Disposition' => 'attachment; filename="' . $attachment->getName() . '"',
  552. 'Content-Length' => $attachment->getSize(),
  553. ]);
  554. }
  555. public function ticketCollaboratorXhr(Request $request)
  556. {
  557. $json = array();
  558. $content = json_decode($request->getContent(), true);
  559. $ticket = $this->em->getRepository(CoreEntities\Ticket::class)->find($content['ticketId']);
  560. $user = $this->userService->getSessionUser();
  561. // process only if access for the resource.
  562. if (empty($ticket) || ((!empty($user)) && $user->getId() != $ticket->getCustomer()->getId())) {
  563. if (! $this->isCollaborator($ticket, $user)) {
  564. throw new \Exception('Access Denied', 403);
  565. }
  566. }
  567. if ($request->getMethod() == "POST") {
  568. if ($content['email'] == $ticket->getCustomer()->getEmail()) {
  569. $json['alertClass'] = 'danger';
  570. $json['alertMessage'] = $this->translator->trans('Error ! Can not add customer as a collaborator.');
  571. } else {
  572. $data = array(
  573. 'from' => $content['email'],
  574. 'firstName' => ($firstName = ucfirst(current(explode('@', $content['email'])))),
  575. 'lastName' => ' ',
  576. 'role' => 4,
  577. );
  578. $supportRole = $this->em->getRepository(CoreEntities\SupportRole::class)->findOneByCode('ROLE_CUSTOMER');
  579. $collaborator = $this->userService->createUserInstance($data['from'], $data['firstName'], $supportRole, $extras = ["active" => true]);
  580. $checkTicket = $this->em->getRepository(CoreEntities\Ticket::class)->isTicketCollaborator($ticket, $content['email']);
  581. if (! $checkTicket) {
  582. $ticket->addCollaborator($collaborator);
  583. $this->em->persist($ticket);
  584. $this->em->flush();
  585. $ticket->lastCollaborator = $collaborator;
  586. $collaborator = $this->em->getRepository(CoreEntities\User::class)->find($collaborator->getId());
  587. $event = new CoreWorkflowEvents\Ticket\Collaborator();
  588. $event
  589. ->setTicket($ticket);
  590. $this->eventDispatcher->dispatch($event, 'uvdesk.automation.workflow.execute');
  591. $json['collaborator'] = $this->userService->getCustomerPartialDetailById($collaborator->getId());
  592. $json['alertClass'] = 'success';
  593. $json['alertMessage'] = $this->translator->trans('Success ! Collaborator added successfully.');
  594. } else {
  595. $json['alertClass'] = 'danger';
  596. $json['alertMessage'] = $this->translator->trans('Error ! Collaborator is already added.');
  597. }
  598. }
  599. } elseif ($request->getMethod() == "DELETE") {
  600. $collaborator = $this->em->getRepository(CoreEntities\User::class)->findOneBy(array('id' => $request->attributes->get('id')));
  601. if ($collaborator) {
  602. $ticket->removeCollaborator($collaborator);
  603. $this->em->persist($ticket);
  604. $this->em->flush();
  605. $json['alertClass'] = 'success';
  606. $json['alertMessage'] = $this->translator->trans('Success ! Collaborator removed successfully.');
  607. } else {
  608. $json['alertClass'] = 'danger';
  609. $json['alertMessage'] = $this->translator->trans('Error ! Invalid Collaborator.');
  610. }
  611. }
  612. $response = new Response(json_encode($json));
  613. $response->headers->set('Content-Type', 'application/json');
  614. return $response;
  615. }
  616. public function ticketIntermediateAccess(Request $request)
  617. {
  618. $user = $this->userService->getSessionUser();
  619. $urid = $request->query->get('urid');
  620. $currentDateTime = new \DateTime('now', new \DateTimeZone('UTC'));
  621. $resource = $this->em->getRepository(CoreEntities\PublicResourceAccessLink::class)->findOneBy([
  622. 'uniqueResourceAccessId' => $urid,
  623. ]);
  624. $user = $resource->getUser();
  625. $website = $this->em->getRepository(CoreEntities\Website::class)->findOneByCode('knowledgebase');
  626. $websiteConfiguration = $this->em->getRepository(SupportEntities\KnowledgebaseWebsite::class)->findOneBy(['website' => $website]);
  627. if (empty($resource)) {
  628. $this->addFlash('warning', $this->translator->trans("Invalid link."));
  629. return $this->redirect($this->generateUrl('helpdesk_knowledgebase'));
  630. }
  631. if (! empty($user)) {
  632. if ($resource->getExpiresAt() < $currentDateTime || $websiteConfiguration->getPublicResourceAccessAttemptLimit() <= $resource->getTotalViews() || $resource->getIsExpired()) {
  633. $resource->setIsExpired(true);
  634. $this->em->persist($resource);
  635. $this->em->flush();
  636. $this->addFlash('warning', $this->translator->trans('Warning! Link expired or access limit reached.'));
  637. return $this->redirect($this->generateUrl('helpdesk_knowledgebase'));
  638. }
  639. if ($resource->getResourceType() == CoreEntities\Ticket::class) {
  640. $ticket = $this->em->getRepository(CoreEntities\Ticket::class)->findOneBy([
  641. 'id' => (int) $resource->getResourceId(),
  642. ]);
  643. $session = $request->getSession();
  644. $viewedLinks = $session->get('accessed_public_links', []);
  645. $ticketId = $ticket->getId();
  646. if (! in_array($ticketId, $viewedLinks)) {
  647. $resource->setTotalViews($resource->getTotalViews() + 1);
  648. $this->em->persist($resource);
  649. $this->em->flush();
  650. $viewedLinks[] = $ticketId;
  651. $session->set('accessed_public_links', $viewedLinks);
  652. }
  653. $token = new UsernamePasswordToken($user, null, 'customer', $user->getRoles());
  654. $this->container->get('security.token_storage')->setToken($token);
  655. $request->getSession()->migrate();
  656. return $this->render('@UVDeskSupportCenter/Knowledgebase/ticketViewPublic.html.twig', [
  657. 'ticket' => $ticket,
  658. 'searchDisable' => true,
  659. 'initialThread' => $this->ticketService->getTicketInitialThreadDetails($ticket),
  660. 'localizedCreateAtTime' => $this->userService->getLocalizedFormattedTime($ticket->getCreatedAt(), $user),
  661. 'isCollaborator' => $this->em->getRepository(CoreEntities\Ticket::class)->isTicketCollaborator($ticket, $user->getEmail()),
  662. ]);
  663. }
  664. }
  665. $this->addFlash('warning', $this->translator->trans("Please login to continue."));
  666. return $this->redirect($this->generateUrl('helpdesk_knowledgebase'));
  667. }
  668. }