src/webkul/uvdesk/core-framework/Controller/Ticket.php line 68

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\Form as CoreFrameworkBundleForms;
  7. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  8. use Webkul\UVDesk\CoreFrameworkBundle\Entity as CoreFrameworkBundleEntities;
  9. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  10. use Webkul\UVDesk\CoreFrameworkBundle\DataProxies as CoreFrameworkBundleDataProxies;
  11. use Webkul\UVDesk\CoreFrameworkBundle\Workflow\Events as CoreWorkflowEvents;
  12. use Webkul\UVDesk\CoreFrameworkBundle\Tickets\QuickActionButtonCollection;
  13. use Webkul\UVDesk\CoreFrameworkBundle\Repository\TicketRepository;
  14. use Webkul\UVDesk\CoreFrameworkBundle\Services\UserService;
  15. use Symfony\Contracts\Translation\TranslatorInterface;
  16. use Webkul\UVDesk\CoreFrameworkBundle\Services\UVDeskService;
  17. use Webkul\UVDesk\CoreFrameworkBundle\Services\TicketService;
  18. use Webkul\UVDesk\CoreFrameworkBundle\Services\EmailService;
  19. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  20. use Symfony\Component\HttpKernel\KernelInterface;
  21. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  22. use Symfony\Component\DependencyInjection\ContainerInterface;
  23. use Webkul\UVDesk\CoreFrameworkBundle\Entity\Attachment;
  24. use Webkul\UVDesk\CoreFrameworkBundle\Entity\Thread;
  25. use Webkul\UVDesk\CoreFrameworkBundle\Entity\Ticket as CoreBundleTicket;
  26. use Webkul\UVDesk\CoreFrameworkBundle\Entity\Tag;
  27. use Webkul\UVDesk\CoreFrameworkBundle\Entity\TicketType;
  28. use Webkul\UVDesk\CoreFrameworkBundle\Entity\SupportRole;
  29. use Webkul\UVDesk\CoreFrameworkBundle\Entity\User;
  30. use Webkul\UVDesk\CoreFrameworkBundle\Entity\TicketPriority;
  31. use Webkul\UVDesk\CoreFrameworkBundle\Entity\TicketStatus;
  32. class Ticket extends AbstractController
  33. {
  34. private $userService;
  35. private $translator;
  36. private $eventDispatcher;
  37. private $ticketService;
  38. private $emailService;
  39. private $kernel;
  40. public function __construct(UserService $userService, TranslatorInterface $translator, TicketService $ticketService, EmailService $emailService, EventDispatcherInterface $eventDispatcher, KernelInterface $kernel)
  41. {
  42. $this->userService = $userService;
  43. $this->emailService = $emailService;
  44. $this->translator = $translator;
  45. $this->ticketService = $ticketService;
  46. $this->eventDispatcher = $eventDispatcher;
  47. $this->kernel = $kernel;
  48. }
  49. public function listTicketCollection(Request $request)
  50. {
  51. $entityManager = $this->getDoctrine()->getManager();
  52. return $this->render('@UVDeskCoreFramework//ticketList.html.twig', [
  53. 'ticketStatusCollection' => $entityManager->getRepository(TicketStatus::class)->findAll(),
  54. 'ticketTypeCollection' => $entityManager->getRepository(TicketType::class)->findByIsActive(true),
  55. 'ticketPriorityCollection' => $entityManager->getRepository(TicketPriority::class)->findAll(),
  56. ]);
  57. }
  58. public function loadTicket($ticketId, QuickActionButtonCollection $quickActionButtonCollection, ContainerInterface $container)
  59. {
  60. $entityManager = $this->getDoctrine()->getManager();
  61. $userRepository = $entityManager->getRepository(User::class);
  62. $ticketRepository = $entityManager->getRepository(CoreBundleTicket::class);
  63. $ticket = $ticketRepository->findOneById($ticketId);
  64. if (empty($ticket)) {
  65. throw new NotFoundHttpException('Page not found!');
  66. }
  67. $user = $this->userService->getSessionUser();
  68. // Proceed only if user has access to the resource
  69. if (false == $this->ticketService->isTicketAccessGranted($ticket, $user)) {
  70. throw new \Exception('Access Denied', 403);
  71. }
  72. $agent = $ticket->getAgent();
  73. $customer = $ticket->getCustomer();
  74. if($agent != null && !empty($agent)){
  75. $ticketAssignAgent = $agent->getId();
  76. $currentUser = $user->getId();
  77. }
  78. // Mark as viewed by agents
  79. if (false == $ticket->getIsAgentViewed()) {
  80. $ticket->setIsAgentViewed(true);
  81. $entityManager->persist($ticket);
  82. $entityManager->flush();
  83. }
  84. // Ticket Authorization
  85. $supportRole = $user->getCurrentInstance()->getSupportRole()->getCode();
  86. switch($supportRole) {
  87. case 'ROLE_ADMIN':
  88. case 'ROLE_SUPER_ADMIN':
  89. break;
  90. case 'ROLE_AGENT':
  91. $accessLevel = (int) $user->getCurrentInstance()->getTicketAccessLevel();
  92. switch($accessLevel) {
  93. case TicketRepository::TICKET_GLOBAL_ACCESS:
  94. break;
  95. case TicketRepository::TICKET_GROUP_ACCESS:
  96. $supportGroups = array_map(function($supportGroup) { return $supportGroup->getId(); }, $user->getCurrentInstance()->getSupportGroups()->getValues());
  97. $ticketAccessableGroups = $ticket->getSupportGroup() ? [$ticket->getSupportGroup()->getId()] : [];
  98. if ($ticket->getSupportTeam()) {
  99. $ticketSupportTeamGroups = array_map(function($supportGroup) { return $supportGroup->getId(); }, $ticket->getSupportTeam()->getSupportGroups()->getValues());
  100. $ticketAccessableGroups = array_merge($ticketAccessableGroups, $ticketSupportTeamGroups);
  101. }
  102. $isAccessableGroupFound = false;
  103. foreach($ticketAccessableGroups as $groupId) {
  104. if (in_array($groupId, $supportGroups)) {
  105. $isAccessableGroupFound = true;
  106. break;
  107. }
  108. }
  109. if (!$isAccessableGroupFound && !($ticketAssignAgent == $currentUser)) {
  110. throw new NotFoundHttpException('Page not found!');
  111. }
  112. break;
  113. case TicketRepository::TICKET_TEAM_ACCESS:
  114. $supportTeams = array_map(function($supportTeam) { return $supportTeam->getId(); }, $user->getCurrentInstance()->getSupportTeams()->getValues());
  115. $supportTeam = $ticket->getSupportTeam();
  116. if (!($supportTeam && in_array($supportTeam->getId(), $supportTeams)) && !($ticketAssignAgent == $currentUser)) {
  117. throw new NotFoundHttpException('Page not found!');
  118. }
  119. break;
  120. default:
  121. $collaborators = array_map( function ($collaborator) { return $collaborator->getId(); }, $ticket->getCollaborators()->getValues());
  122. $accessableAgents = array_merge($collaborators, $ticket->getAgent() ? [$ticket->getAgent()->getId()] : []);
  123. if (!in_array($user->getId(), $accessableAgents)) {
  124. throw new NotFoundHttpException('Page not found!');
  125. }
  126. break;
  127. }
  128. break;
  129. default:
  130. throw new NotFoundHttpException('Page not found!');
  131. }
  132. $quickActionButtonCollection->prepareAssets();
  133. // dd($ticket);
  134. return $this->render('@UVDeskCoreFramework//ticket.html.twig', [
  135. 'ticket' => $ticket,
  136. 'totalReplies' => $ticketRepository->countTicketTotalThreads($ticket->getId()),
  137. 'totalCustomerTickets' => ($ticketRepository->countCustomerTotalTickets($customer, $container) - 1),
  138. 'initialThread' => $this->ticketService->getTicketInitialThreadDetails($ticket),
  139. 'ticketAgent' => !empty($agent) ? $agent->getAgentInstance()->getPartialDetails() : null,
  140. 'customer' => $customer->getCustomerInstance()->getPartialDetails(),
  141. 'currentUserDetails' => $user->getAgentInstance()->getPartialDetails(),
  142. 'supportGroupCollection' => $userRepository->getSupportGroups(),
  143. 'supportTeamCollection' => $userRepository->getSupportTeams(),
  144. 'ticketStatusCollection' => $entityManager->getRepository(TicketStatus::class)->findAll(),
  145. 'ticketTypeCollection' => $entityManager->getRepository(TicketType::class)->findByIsActive(true),
  146. 'ticketPriorityCollection' => $entityManager->getRepository(TicketPriority::class)->findAll(),
  147. 'ticketNavigationIteration' => $ticketRepository->getTicketNavigationIteration($ticket, $container),
  148. 'ticketLabelCollection' => $ticketRepository->getTicketLabelCollection($ticket, $user),
  149. ]);
  150. }
  151. public function saveTicket(Request $request)
  152. {
  153. $requestParams = $request->request->all();
  154. $entityManager = $this->getDoctrine()->getManager();
  155. $response = $this->redirect($this->generateUrl('helpdesk_member_ticket_collection'));
  156. if ($request->getMethod() != 'POST' || false == $this->userService->isAccessAuthorized('ROLE_AGENT_CREATE_TICKET')) {
  157. return $response;
  158. }
  159. // Get referral ticket if any
  160. $ticketValidationGroup = 'CreateTicket';
  161. $referralURL = $request->headers->get('referer');
  162. if (!empty($referralURL)) {
  163. $iterations = explode('/', $referralURL);
  164. $referralId = array_pop($iterations);
  165. $expectedReferralURL = $this->generateUrl('helpdesk_member_ticket', ['ticketId' => $referralId], UrlGeneratorInterface::ABSOLUTE_URL);
  166. if ($referralURL === $expectedReferralURL) {
  167. $referralTicket = $entityManager->getRepository(CoreBundleTicket::class)->findOneById($referralId);
  168. if (!empty($referralTicket)) {
  169. $ticketValidationGroup = 'CustomerCreateTicket';
  170. }
  171. }
  172. }
  173. $ticketType = $entityManager->getRepository(TicketType::class)->findOneById($requestParams['type']);
  174. try {
  175. if ($this->userService->isfileExists('apps/uvdesk/custom-fields')) {
  176. $customFieldsService = $this->get('uvdesk_package_custom_fields.service');
  177. } else if ($this->userService->isfileExists('apps/uvdesk/form-component')) {
  178. $customFieldsService = $this->get('uvdesk_package_form_component.service');
  179. }
  180. if (!empty($customFieldsService)) {
  181. extract($customFieldsService->customFieldsValidation($request, 'user'));
  182. }
  183. } catch (\Exception $e) {
  184. // @TODO: Log execption message
  185. }
  186. if(!empty($errorFlashMessage)) {
  187. $this->addFlash('warning', $errorFlashMessage);
  188. }
  189. $ticketProxy = new CoreFrameworkBundleDataProxies\CreateTicketDataClass();
  190. $form = $this->createForm(CoreFrameworkBundleForms\CreateTicket::class, $ticketProxy);
  191. // Validate Ticket Details
  192. $form->submit($requestParams);
  193. if (false == $form->isSubmitted() || false == $form->isValid()) {
  194. if (false === $form->isValid()) {
  195. // @TODO: We need to handle form errors gracefully.
  196. // We should also look into switching to an xhr request instead.
  197. // $form->getErrors(true);
  198. }
  199. return $this->redirect(!empty($referralURL) ? $referralURL : $this->generateUrl('helpdesk_member_ticket_collection'));
  200. }
  201. if ('CustomerCreateTicket' === $ticketValidationGroup && !empty($referralTicket)) {
  202. // Retrieve customer details from referral ticket
  203. $customer = $referralTicket->getCustomer();
  204. $customerPartialDetails = $customer->getCustomerInstance()->getPartialDetails();
  205. } else if (null != $ticketProxy->getFrom() && null != $ticketProxy->getName()) {
  206. // Create customer if account does not exists
  207. $customer = $entityManager->getRepository(User::class)->findOneByEmail($ticketProxy->getFrom());
  208. if (empty($customer) || null == $customer->getCustomerInstance()) {
  209. $role = $entityManager->getRepository(SupportRole::class)->findOneByCode('ROLE_CUSTOMER');
  210. // Create User Instance
  211. $customer = $this->userService->createUserInstance($ticketProxy->getFrom(), $ticketProxy->getName(), $role, [
  212. 'source' => 'website',
  213. 'active' => true
  214. ]);
  215. }
  216. }
  217. $ticketData = [
  218. 'from' => $customer->getEmail(),
  219. 'name' => $customer->getFirstName() . ' ' . $customer->getLastName(),
  220. 'type' => $ticketProxy->getType(),
  221. 'subject' => $ticketProxy->getSubject(),
  222. // @TODO: We need to enable support for html messages.
  223. // Our focus here instead should be to prevent XSS (filter js)
  224. 'message' => str_replace(['&lt;script&gt;', '&lt;/script&gt;'], '', htmlspecialchars($ticketProxy->getReply())),
  225. 'firstName' => $customer->getFirstName(),
  226. 'lastName' => $customer->getLastName(),
  227. 'type' => $ticketProxy->getType(),
  228. 'role' => 4,
  229. 'source' => 'website',
  230. 'threadType' => 'create',
  231. 'createdBy' => 'agent',
  232. 'customer' => $customer,
  233. 'user' => $this->getUser(),
  234. 'attachments' => $request->files->get('attachments'),
  235. ];
  236. $thread = $this->ticketService->createTicketBase($ticketData);
  237. // Trigger ticket created event
  238. try {
  239. $event = new CoreWorkflowEvents\Ticket\Create();
  240. $event
  241. ->setTicket($thread->getTicket())
  242. ;
  243. $this->eventDispatcher->dispatch($event, 'uvdesk.automation.workflow.execute');
  244. } catch (\Exception $e) {
  245. // Skip Automation
  246. }
  247. if (!empty($thread)) {
  248. $ticket = $thread->getTicket();
  249. if($request->request->get('customFields') || $request->files->get('customFields')) {
  250. $this->ticketService->addTicketCustomFields($thread, $request->request->get('customFields'), $request->files->get('customFields'));
  251. }
  252. $this->addFlash('success', $this->translator->trans('Success ! Ticket has been created successfully.'));
  253. if ($this->userService->isAccessAuthorized('ROLE_ADMIN')) {
  254. return $this->redirect($this->generateUrl('helpdesk_member_ticket', ['ticketId' => $ticket->getId()]));
  255. }
  256. } else {
  257. $this->addFlash('warning', $this->translator->trans('Could not create ticket, invalid details.'));
  258. }
  259. return $this->redirect(!empty($referralURL) ? $referralURL : $this->generateUrl('helpdesk_member_ticket_collection'));
  260. }
  261. public function listTicketTypeCollection(Request $request)
  262. {
  263. if (!$this->userService->isAccessAuthorized('ROLE_AGENT_MANAGE_TICKET_TYPE')) {
  264. return $this->redirect($this->generateUrl('helpdesk_member_dashboard'));
  265. }
  266. return $this->render('@UVDeskCoreFramework/ticketTypeList.html.twig');
  267. }
  268. public function ticketType(Request $request)
  269. {
  270. if (!$this->userService->isAccessAuthorized('ROLE_AGENT_MANAGE_TICKET_TYPE')) {
  271. return $this->redirect($this->generateUrl('helpdesk_member_dashboard'));
  272. }
  273. $errorContext = [];
  274. $em = $this->getDoctrine()->getManager();
  275. if($id = $request->attributes->get('ticketTypeId')) {
  276. $type = $em->getRepository(TicketType::class)->find($id);
  277. if (!$type) {
  278. $this->noResultFound();
  279. }
  280. } else {
  281. $type = new CoreFrameworkBundleEntities\TicketType();
  282. }
  283. if ($request->getMethod() == "POST") {
  284. $data = $request->request->all();
  285. $ticketType = $em->getRepository(TicketType::class)->findOneByCode($data['code']);
  286. if (!empty($ticketType) && $id != $ticketType->getId()) {
  287. $this->addFlash('warning', sprintf('Error! Ticket type with same name already exist'));
  288. } else {
  289. $type->setCode($data['code']);
  290. $type->setDescription($data['description']);
  291. $type->setIsActive(isset($data['isActive']) ? 1 : 0);
  292. $em->persist($type);
  293. $em->flush();
  294. if (!$request->attributes->get('ticketTypeId')) {
  295. $this->addFlash('success', $this->translator->trans('Success! Ticket type saved successfully.'));
  296. } else {
  297. $this->addFlash('success', $this->translator->trans('Success! Ticket type updated successfully.'));
  298. }
  299. return $this->redirect($this->generateUrl('helpdesk_member_ticket_type_collection'));
  300. }
  301. }
  302. return $this->render('@UVDeskCoreFramework/ticketTypeAdd.html.twig', array(
  303. 'type' => $type,
  304. 'errors' => json_encode($errorContext)
  305. ));
  306. }
  307. public function listTagCollection(Request $request)
  308. {
  309. if (!$this->userService->isAccessAuthorized('ROLE_AGENT_MANAGE_TAG')) {
  310. return $this->redirect($this->generateUrl('helpdesk_member_dashboard'));
  311. }
  312. $enabled_bundles = $this->getParameter('kernel.bundles');
  313. return $this->render('@UVDeskCoreFramework/supportTagList.html.twig', [
  314. 'articlesEnabled' => in_array('UVDeskSupportCenterBundle', array_keys($enabled_bundles)),
  315. ]);
  316. }
  317. public function removeTicketTagXHR($tagId, Request $request)
  318. {
  319. if (!$this->userService->isAccessAuthorized('ROLE_AGENT_MANAGE_TAG')) {
  320. return $this->redirect($this->generateUrl('helpdesk_member_dashboard'));
  321. }
  322. $json = [];
  323. if($request->getMethod() == "DELETE") {
  324. $em = $this->getDoctrine()->getManager();
  325. $tag = $em->getRepository(Tag::class)->find($tagId);
  326. if($tag) {
  327. $em->remove($tag);
  328. $em->flush();
  329. $json['alertClass'] = 'success';
  330. $json['alertMessage'] = $this->translator->trans('Success ! Tag removed successfully.');
  331. }
  332. }
  333. $response = new Response(json_encode($json));
  334. $response->headers->set('Content-Type', 'application/json');
  335. return $response;
  336. }
  337. public function trashTicket(Request $request)
  338. {
  339. $ticketId = $request->attributes->get('ticketId');
  340. $entityManager = $this->getDoctrine()->getManager();
  341. $ticket = $entityManager->getRepository(CoreBundleTicket::class)->find($ticketId);
  342. if (!$ticket) {
  343. $this->noResultFound();
  344. }
  345. $user = $this->userService->getSessionUser();
  346. // Proceed only if user has access to the resource
  347. if (false == $this->ticketService->isTicketAccessGranted($ticket, $user)) {
  348. throw new \Exception('Access Denied', 403);
  349. }
  350. if (!$ticket->getIsTrashed()) {
  351. $ticket->setIsTrashed(1);
  352. $entityManager->persist($ticket);
  353. $entityManager->flush();
  354. }
  355. // Trigger ticket delete event
  356. $event = new CoreWorkflowEvents\Ticket\Delete();
  357. $event
  358. ->setTicket($ticket)
  359. ;
  360. $this->eventDispatcher->dispatch($event, 'uvdesk.automation.workflow.execute');
  361. $this->addFlash('success', $this->translator->trans('Success ! Ticket moved to trash successfully.'));
  362. return $this->redirectToRoute('helpdesk_member_ticket_collection');
  363. }
  364. // Delete a ticket ticket permanently
  365. public function deleteTicket(Request $request)
  366. {
  367. $ticketId = $request->attributes->get('ticketId');
  368. $entityManager = $this->getDoctrine()->getManager();
  369. $ticket = $entityManager->getRepository(CoreBundleTicket::class)->find($ticketId);
  370. if (!$ticket) {
  371. $this->noResultFound();
  372. }
  373. $user = $this->userService->getSessionUser();
  374. // Proceed only if user has access to the resource
  375. if (false == $this->ticketService->isTicketAccessGranted($ticket, $user)) {
  376. throw new \Exception('Access Denied', 403);
  377. }
  378. $entityManager->remove($ticket);
  379. $entityManager->flush();
  380. $this->addFlash('success', $this->translator->trans('Success ! Success ! Ticket Id #'. $ticketId .' has been deleted successfully.'));
  381. return $this->redirectToRoute('helpdesk_member_ticket_collection');
  382. }
  383. public function downloadZipAttachment(Request $request)
  384. {
  385. $threadId = $request->attributes->get('threadId');
  386. $attachmentRepository = $this->getDoctrine()->getManager()->getRepository(Attachment::class);
  387. $threadRepository = $this->getDoctrine()->getManager()->getRepository(Thread::class);
  388. $thread = $threadRepository->findOneById($threadId);
  389. $attachment = $attachmentRepository->findByThread($threadId);
  390. if (!$attachment) {
  391. $this->noResultFound();
  392. }
  393. $ticket = $thread->getTicket();
  394. $user = $this->userService->getSessionUser();
  395. // Proceed only if user has access to the resource
  396. if (false == $this->ticketService->isTicketAccessGranted($ticket, $user)) {
  397. throw new \Exception('Access Denied', 403);
  398. }
  399. $zipname = 'attachments/' .$threadId.'.zip';
  400. $zip = new \ZipArchive;
  401. $zip->open($zipname, \ZipArchive::CREATE);
  402. if (count($attachment)) {
  403. foreach ($attachment as $attach) {
  404. $zip->addFile(substr($attach->getPath(), 1));
  405. }
  406. }
  407. $zip->close();
  408. $response = new Response();
  409. $response->setStatusCode(200);
  410. $response->headers->set('Content-type', 'application/zip');
  411. $response->headers->set('Content-Disposition', 'attachment; filename=' . $threadId . '.zip');
  412. $response->sendHeaders();
  413. $response->setContent(readfile($zipname));
  414. return $response;
  415. }
  416. public function downloadAttachment(Request $request)
  417. {
  418. $attachmentId = $request->attributes->get('attachmendId');
  419. $attachment = $this->getDoctrine()->getManager()->getRepository(Attachment::class)->findOneById($attachmentId);
  420. $baseurl = $request->getScheme() . '://' . $request->getHttpHost() . $request->getBasePath();
  421. if (empty($attachment)) {
  422. $this->noResultFound();
  423. }
  424. $thread = $attachment->getThread();
  425. if (!empty($thread)) {
  426. $ticket = $thread->getTicket();
  427. $user = $this->userService->getSessionUser();
  428. // Proceed only if user has access to the resource
  429. if (false == $this->ticketService->isTicketAccessGranted($ticket, $user)) {
  430. throw new \Exception('Access Denied', 403);
  431. }
  432. }
  433. $path = $this->kernel->getProjectDir() . "/public/". $attachment->getPath();
  434. $response = new Response();
  435. $response->headers->set('Content-type', $attachment->getContentType());
  436. $response->headers->set('Content-Disposition', 'attachment; filename='. $attachment->getName());
  437. $response->headers->set('Content-Length', $attachment->getSize());
  438. $response->setStatusCode(200);
  439. $response->sendHeaders();
  440. $response->setContent(readfile($path));
  441. return $response;
  442. }
  443. /**
  444. * If customer is playing with url and no result is found then what will happen
  445. * @return
  446. */
  447. protected function noResultFound()
  448. {
  449. throw new NotFoundHttpException('Not Found!');
  450. }
  451. }