vendor/uvdesk/core-framework/Controller/Ticket.php line 410

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\CommunityPackages\UVDesk\CustomFields\Entity;
  7. use Webkul\UVDesk\CommunityPackages\UVDesk\CustomFields\Entity as CommunityPackageEntities;
  8. use Webkul\UVDesk\CoreFrameworkBundle\Form as CoreFrameworkBundleForms;
  9. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  10. use Webkul\UVDesk\AutomationBundle\Entity\Workflow;
  11. use Webkul\UVDesk\CoreFrameworkBundle\Entity as CoreFrameworkBundleEntities;
  12. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  13. use Webkul\UVDesk\CoreFrameworkBundle\DataProxies as CoreFrameworkBundleDataProxies;
  14. use Webkul\UVDesk\CoreFrameworkBundle\Workflow\Events as CoreWorkflowEvents;
  15. use Webkul\UVDesk\CoreFrameworkBundle\Tickets\QuickActionButtonCollection;
  16. use Webkul\UVDesk\CoreFrameworkBundle\Repository\TicketRepository;
  17. use Webkul\UVDesk\CoreFrameworkBundle\Services\UserService;
  18. use Symfony\Contracts\Translation\TranslatorInterface;
  19. use Webkul\UVDesk\CoreFrameworkBundle\Services\UVDeskService;
  20. use Webkul\UVDesk\CoreFrameworkBundle\Services\TicketService;
  21. use Webkul\UVDesk\CoreFrameworkBundle\Services\EmailService;
  22. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  23. use Symfony\Component\HttpKernel\KernelInterface;
  24. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  25. use Symfony\Component\DependencyInjection\ContainerInterface;
  26. use Webkul\UVDesk\CoreFrameworkBundle\Entity\Attachment;
  27. use Webkul\UVDesk\CoreFrameworkBundle\Entity\Thread;
  28. use Webkul\UVDesk\CoreFrameworkBundle\Entity\Ticket as CoreBundleTicket;
  29. use Webkul\UVDesk\CoreFrameworkBundle\Entity\Tag;
  30. use Webkul\UVDesk\CoreFrameworkBundle\Entity\TicketType;
  31. use Webkul\UVDesk\CoreFrameworkBundle\Entity\SupportRole;
  32. use Webkul\UVDesk\CoreFrameworkBundle\Entity\User;
  33. use Webkul\UVDesk\CoreFrameworkBundle\Entity\TicketPriority;
  34. use Webkul\UVDesk\CoreFrameworkBundle\Entity\TicketStatus;
  35. use Webkul\UVDesk\MailboxBundle\Services\MailboxService;
  36. use UVDesk\CommunityPackages\UVDesk\CustomFields\Services\CustomFieldsService;
  37. use Doctrine\DBAL\Connection;
  38. use Symfony\Component\HttpFoundation\JsonResponse;
  39. class Ticket extends AbstractController
  40. {
  41.     private $userService;
  42.     private $translator;
  43.     private $eventDispatcher;
  44.     private $ticketService;
  45.     private $emailService;
  46.     private $kernel;
  47.     private $customFieldsService;
  48.     public function __construct(UserService $userServiceTranslatorInterface $translatorTicketService $ticketServiceEmailService $emailServiceEventDispatcherInterface $eventDispatcherKernelInterface $kernel,CustomFieldsService $customFieldsService)
  49.     {
  50.         $this->userService $userService;
  51.         $this->emailService $emailService;
  52.         $this->translator $translator;
  53.         $this->ticketService $ticketService;
  54.         $this->eventDispatcher $eventDispatcher;
  55.         $this->kernel $kernel;
  56.         $this->customFieldsService $customFieldsService;
  57.     }
  58.     public function listTicketCollection(Request $requestMailboxService $mailboxService)
  59.     {
  60.         $entityManager $this->getDoctrine()->getManager();
  61.          $connection $entityManager->getConnection();
  62.         $ticketEntities $this->getDoctrine()->getRepository(CoreBundleTicket::class)->findAll();
  63.                 // Create an array to hold the IDs
  64.                 $allWorkflowData = [];
  65.         // Loop through each ticket
  66.         foreach ($ticketEntities as $ticket) {
  67.             $ticketId $ticket->getId();
  68.             // Retrieve the tags for the ticket
  69.             $tags $this->ticketService->getTicketTagsById($ticketId);
  70.             // Only proceed if tags exist
  71.             if (!empty($tags)) {
  72.                 $tagName $tags[0]['name'];
  73.                 
  74.                 // SQL query to fetch workflow data for this ticket
  75.                 $sql "
  76.                     SELECT 
  77.                         w.name AS left_side, 
  78.                         w.wf_tag AS center, 
  79.                         w.priority AS right_side,
  80.                         t.code,
  81.                         th.status_update
  82.                     FROM uv_workflow w
  83.                     LEFT JOIN uv_ticket_type t ON w.name = t.code
  84.                     LEFT JOIN uv_thread th ON t.id = th.type_id AND th.ticket_id = :ticketId
  85.                     WHERE w.wf_tag LIKE :tagName
  86.                     ORDER BY CAST(w.priority AS UNSIGNED) ASC
  87.                     LIMIT 0, 1000
  88.                 ";
  89.                 $stmt $connection->prepare($sql);
  90.                 $stmt->execute(['ticketId' => $ticketId'tagName'  => $tagName]);
  91.                 // Fetch all matching rows for this ticket
  92.                 $workflowData $stmt->fetchAll();
  93.             } else {
  94.                 // If no tags, assign an empty array (so that it's defined)
  95.                 $workflowData = [];
  96.             }
  97.             // Save the workflow data for this ticket ID in the overall array
  98.             $allWorkflowData[$ticketId] = $workflowData;
  99.         }
  100.         $mailboxCollection = [];
  101.         $mailboxConfiguration $mailboxService->parseMailboxConfigurations();
  102.         
  103.         foreach ($mailboxConfiguration->getMailboxes() as $mailbox) {
  104.             $imapConfiguration $mailbox->getImapConfiguration();
  105.             if (!empty($imapConfiguration)) {
  106.                 $mailboxCollection[] = [
  107.                     'name' => $mailbox->getName(), 
  108.                     'email' => $imapConfiguration->getUsername(), 
  109.                 ];
  110.             }
  111.         }
  112.         return $this->render('@UVDeskCoreFramework//ticketList.html.twig', [
  113.             'mailboxCollection' => $mailboxCollection
  114.             'ticketStatusCollection' => $entityManager->getRepository(TicketStatus::class)->findAll(), 
  115.             'ticketTypeCollection' => $entityManager->getRepository(TicketType::class)->findByIsActive(true), 
  116.             'ticketPriorityCollection' => $entityManager->getRepository(TicketPriority::class)->findAll(), 
  117.             'allWorkflowData' => $allWorkflowData,
  118.         ]);
  119.     }
  120.     public function loadTicket($ticketIdQuickActionButtonCollection $quickActionButtonCollectionContainerInterface $container)
  121.     {
  122.         $entityManager $this->getDoctrine()->getManager();
  123.         $connection $entityManager->getConnection();
  124.         $userRepository $entityManager->getRepository(User::class);
  125.         $workflow $entityManager->getRepository(Workflow::class);
  126.         $ticketRepository $entityManager->getRepository(CoreBundleTicket::class);
  127.         $ticket $ticketRepository->findOneById($ticketId);
  128.         $tags $this->ticketService->getTicketTagsById($ticketId);
  129.         if (isset($tags[0]['name'])) {
  130.             $tagName $tags[0]['name'];
  131.         } else {
  132.             $tagName 'No Tag Available'// Handle it properly
  133.         }
  134.         //$tagName = $tags[0]->name;
  135.         
  136.         if (empty($ticket)) {
  137.             throw new NotFoundHttpException('Page not found!');
  138.         }
  139.         
  140.         $user $this->userService->getSessionUser();
  141.         $timeSpent $ticket->getTimeSpent();
  142.         if (!empty($tags)) {
  143.             $sql "
  144.             SELECT 
  145.                 w.name AS left_side, 
  146.                 w.wf_tag AS center, 
  147.                 w.priority AS right_side,
  148.                 t.code,
  149.                 th.status_update
  150.             FROM uv_workflow w
  151.             LEFT JOIN uv_ticket_type t ON w.name = t.code
  152.             LEFT JOIN uv_thread th ON t.id = th.type_id AND th.ticket_id = :ticketId
  153.             WHERE w.wf_tag LIKE :tagName
  154.             ORDER BY CAST(w.priority AS UNSIGNED) ASC
  155.             LIMIT 0, 1000
  156.         ";
  157.         $stmt $connection->prepare($sql);
  158.         $stmt->execute(['ticketId' => $ticketId'tagName'  => $tagName]);
  159.             
  160.             // Fetch all matching rows
  161.             $workflowData $stmt->fetchAll();
  162.         } else {
  163.             // If there are no tags available, handle accordingly
  164.             $workflowData null// or you could set it to an empty array []
  165.         }
  166.         
  167.         $sql "SELECT custom_field_id, value
  168.         FROM uv_pkg_uvdesk_form_component_ticket_custom_fields_values
  169.         WHERE ticket_id = :ticketId
  170.           AND thread_id IS NULL";
  171.         // Prepare the statement
  172.         $stmt $connection->prepare($sql);
  173.         // Execute the query with the bound ticket id
  174.         $stmt->execute([
  175.             ':ticketId' => $ticketId
  176.         ]);
  177.         // Fetch all matching rows as an associative array
  178.         $ticketcustomFields $stmt->fetchAll();
  179.         
  180.         // Proceed only if user has access to the resource
  181.         if (false == $this->ticketService->isTicketAccessGranted($ticket$user)) {
  182.             throw new \Exception('Access Denied'403);
  183.         }
  184.         $agent $ticket->getAgent();
  185.         $customer $ticket->getCustomer();
  186.      
  187.     if($agent != null && !empty($agent)){    
  188.         $ticketAssignAgent $agent->getId();
  189.         $currentUser $user->getId();
  190.     }
  191.         
  192.         // Mark as viewed by agents
  193.         if (false == $ticket->getIsAgentViewed()) {
  194.             $ticket->setIsAgentViewed(true);
  195.             $entityManager->persist($ticket);
  196.             $entityManager->flush();
  197.         }
  198.     
  199.         // Ticket Authorization
  200.         $supportRole $user->getCurrentInstance()->getSupportRole()->getCode(); 
  201.         switch($supportRole) {
  202.             case 'ROLE_ADMIN':
  203.             case 'ROLE_SUPER_ADMIN':
  204.                 break;
  205.             case 'ROLE_AGENT':
  206.                 $accessLevel = (int) $user->getCurrentInstance()->getTicketAccessLevel();
  207.                 switch($accessLevel) {
  208.                     case TicketRepository::TICKET_GLOBAL_ACCESS:
  209.                         break;
  210.                     case TicketRepository::TICKET_GROUP_ACCESS:
  211.                         $supportGroups array_map(function($supportGroup) { return $supportGroup->getId(); }, $user->getCurrentInstance()->getSupportGroups()->getValues());                       
  212.                         $ticketAccessableGroups $ticket->getSupportGroup() ? [$ticket->getSupportGroup()->getId()] : [];
  213.  
  214.                         if ($ticket->getSupportTeam()) {
  215.                             $ticketSupportTeamGroups array_map(function($supportGroup) { return $supportGroup->getId(); }, $ticket->getSupportTeam()->getSupportGroups()->getValues());
  216.                             $ticketAccessableGroups array_merge($ticketAccessableGroups$ticketSupportTeamGroups);
  217.                         }
  218.                         $isAccessableGroupFound false;
  219.                         foreach($ticketAccessableGroups as $groupId) {
  220.                             if (in_array($groupId$supportGroups)) {
  221.                                 $isAccessableGroupFound true;
  222.                                 break;
  223.                             }
  224.                         }
  225.                         if (!$isAccessableGroupFound && !($ticketAssignAgent == $currentUser)) {
  226.                             throw new NotFoundHttpException('Page not found!');
  227.                         }
  228.                         break;
  229.                     case TicketRepository::TICKET_TEAM_ACCESS:
  230.                         $supportTeams array_map(function($supportTeam) { return $supportTeam->getId(); }, $user->getCurrentInstance()->getSupportTeams()->getValues());                         
  231.                         $supportTeam $ticket->getSupportTeam();
  232.                         if (!($supportTeam && in_array($supportTeam->getId(), $supportTeams)) && !($ticketAssignAgent == $currentUser)) {
  233.                             throw new NotFoundHttpException('Page not found!');
  234.                         }
  235.                         break;
  236.                     default:
  237.                         $collaborators array_map( function ($collaborator) { return $collaborator->getId(); }, $ticket->getCollaborators()->getValues());
  238.                         $accessableAgents array_merge($collaborators$ticket->getAgent() ? [$ticket->getAgent()->getId()] : []);
  239.                         if (!in_array($user->getId(), $accessableAgents)) {
  240.                             throw new NotFoundHttpException('Page not found!');
  241.                         }
  242.                         break;
  243.                 }
  244.                 break;
  245.             default:
  246.                 throw new NotFoundHttpException('Page not found!');
  247.         }
  248.         $quickActionButtonCollection->prepareAssets();
  249.         
  250.         try {
  251.     if ($this->userService->isfileExists('apps/uvdesk/custom-fields')) {
  252.         $headerCustomFields $this->customFieldsService->getCustomFieldsArray('user');;
  253.     } else if ($this->userService->isfileExists('apps/uvdesk/form-component')) {
  254.         $headerCustomFields $this->customFieldsService->getCustomFieldsArray('user');;
  255.     }
  256. } catch (\Exception $e) {
  257.     // @TODO: Log execption message
  258. }
  259.         return $this->render('@UVDeskCoreFramework//ticket.html.twig', [
  260.             'ticket' => $ticket,
  261.             'timeSpent' => $timeSpent,
  262.             'totalReplies' => $ticketRepository->countTicketTotalThreads($ticket->getId()),
  263.             'totalCustomerTickets' => ($ticketRepository->countCustomerTotalTickets($customer$container) - 1),
  264.             'initialThread' => $this->ticketService->getTicketInitialThreadDetails($ticket),
  265.             'ticketAgent' => !empty($agent) ? $agent->getAgentInstance()->getPartialDetails() : null,
  266.             'customer' => $customer->getCustomerInstance()->getPartialDetails(),
  267.             'currentUserDetails' => $user->getAgentInstance()->getPartialDetails(),
  268.             'supportGroupCollection' => $userRepository->getSupportGroups(),
  269.             'supportTeamCollection' => $userRepository->getSupportTeams(),
  270.             'ticketStatusCollection' => $entityManager->getRepository(TicketStatus::class)->findAll(),
  271.             'ticketTypeCollection' => $entityManager->getRepository(TicketType::class)->findByIsActive(true),
  272.             'ticketPriorityCollection' => $entityManager->getRepository(TicketPriority::class)->findAll(),
  273.             'ticketNavigationIteration' => $ticketRepository->getTicketNavigationIteration($ticket$container),
  274.             'ticketLabelCollection' => $ticketRepository->getTicketLabelCollection($ticket$user),
  275.             'headerCustomFields' => $headerCustomFields ?? null,
  276.             'workflowData' => $workflowData,
  277.             'ticketcustomFields' => $ticketcustomFields,
  278.         ]);
  279.     }
  280.     
  281.      public function showWorkflow(Request $request): JsonResponse
  282.     {
  283.         $entityManager $this->getDoctrine()->getManager();
  284.         $connection $entityManager->getConnection();
  285.         $ticketId $request->query->get('ticketId'0); // 0 is the default if not provided
  286.         // Retrieve all workflow entities (adjust the method as needed)
  287.         $ticketRepository $entityManager->getRepository(CoreBundleTicket::class);
  288.         $ticket $ticketRepository->findOneById($ticketId);
  289.         $tags $this->ticketService->getTicketTagsById($ticketId);
  290.         $tagName $tags[0]['name'];
  291.         //$tagName = $tags[0]->name;
  292.         
  293.         if (empty($ticket)) {
  294.             throw new NotFoundHttpException('Page not found!');
  295.         }
  296.         
  297.         $user $this->userService->getSessionUser();
  298.         if (!empty($tags)) {
  299.             $sql "
  300.             SELECT 
  301.                 w.name AS left_side, 
  302.                 w.wf_tag AS center, 
  303.                 w.priority AS right_side,
  304.                 t.code,
  305.                 th.status_update
  306.             FROM uv_workflow w
  307.             LEFT JOIN uv_ticket_type t ON w.name = t.code
  308.             LEFT JOIN uv_thread th ON t.id = th.type_id AND th.ticket_id = :ticketId
  309.             WHERE w.wf_tag LIKE :tagName
  310.             ORDER BY CAST(w.priority AS UNSIGNED) ASC
  311.             LIMIT 0, 1000
  312.         ";
  313.         $stmt $connection->prepare($sql);
  314.         $stmt->execute(['ticketId' => $ticketId'tagName'  => $tagName]);
  315.             
  316.             // Fetch all matching rows
  317.             $workflow $stmt->fetchAll();
  318.         } else {
  319.             // If there are no tags available, handle accordingly
  320.             $workflow null// or you could set it to an empty array []
  321.         }
  322.          // Define the SQL query with a join between uv_ticket and uv_ticket_type
  323.          $sql '
  324.          SELECT ut.code
  325.          FROM uv_ticket t
  326.          JOIN uv_ticket_type ut ON t.type_id = ut.id
  327.          WHERE t.id = :ticketId
  328.         ';
  329.         // Prepare and execute the query
  330.         $stmt $connection->prepare($sql);
  331.         $stmt->bindValue('ticketId'$ticketId);
  332.         $stmt->execute();
  333.         // Fetch the code (assuming there is only one result)
  334.         $code $stmt->fetchColumn();
  335.         // Combine workflow data and code into one response array
  336.         $response = [
  337.             'workflow' => $workflow,
  338.             'code'     => $code
  339.         ];
  340.         return new JsonResponse($response);
  341.         
  342.     }
  343.     
  344.     
  345.     public function fetchEmails(Request $requestConnection $connection) {
  346.         $entityManager $this->getDoctrine()->getManager();
  347.         $query $request->query->get('query');
  348.         // SQL query to fetch emails based on the input query
  349.         $sql "
  350.             SELECT u.email
  351.             FROM uv_user_instance ui
  352.             JOIN uv_user u ON ui.user_id = u.id
  353.             WHERE ui.supportRole_id IN (3, 4) AND u.email LIKE :query
  354.         ";
  355.         $statement $connection->prepare($sql);
  356.         $statement->execute(['query' => '%' $query '%']);
  357.         $emails $statement->fetchAll();
  358.         // Extract emails from the result
  359.         $emailList array_map(function($email) {
  360.             return $email['email'];
  361.         }, $emails);
  362.         return new JsonResponse($emailList);
  363.     }
  364.       public function getUserFullNames(Request $request): JsonResponse
  365.     {
  366.         // Obtain the Doctrine connection
  367.         $entityManager $this->getDoctrine()->getManager();
  368.         $connection $entityManager->getConnection();
  369.         $queryText $request->query->get('query''');
  370.         // Define the SQL query to combine first_name and last_name
  371.         $sql "SELECT CONCAT(first_name, ' ', last_name) AS full_name
  372.             FROM uv_user
  373.             WHERE CONCAT(first_name, ' ', last_name) LIKE :query";
  374.     
  375.         // Prepare and execute the query without binding any parameters
  376.         $stmt $connection->prepare($sql);
  377.         $stmt->execute(['query' => '%' $queryText '%']);
  378.         // Fetch all results (each row will have a key 'full_name')
  379.         $fullNames $stmt->fetchAll();
  380.         // Return the results as a JSON response
  381.         return new JsonResponse($fullNames);
  382.     }    
  383.     public function saveTicket(Request $request)
  384.     {
  385.         $requestParams $request->request->all();
  386.         $entityManager $this->getDoctrine()->getManager();
  387.         $response $this->redirect($this->generateUrl('helpdesk_member_ticket_collection'));
  388.         if ($request->getMethod() != 'POST' || false == $this->userService->isAccessAuthorized('ROLE_AGENT_CREATE_TICKET')) {
  389.             return $response;
  390.         }
  391.         // Get referral ticket if any
  392.         $ticketValidationGroup 'CreateTicket';
  393.         $referralURL $request->headers->get('referer');
  394.         if (!empty($referralURL)) {
  395.             $iterations explode('/'$referralURL);
  396.             $referralId array_pop($iterations);
  397.             $expectedReferralURL $this->generateUrl('helpdesk_member_ticket', ['ticketId' => $referralId], UrlGeneratorInterface::ABSOLUTE_URL);
  398.             if ($referralURL === $expectedReferralURL) {
  399.                 $referralTicket $entityManager->getRepository(CoreBundleTicket::class)->findOneById($referralId);
  400.                 if (!empty($referralTicket)) {
  401.                     $ticketValidationGroup 'CustomerCreateTicket';
  402.                 }
  403.             }
  404.         }
  405.         $ticketType $entityManager->getRepository(TicketType::class)->findOneById($requestParams['type']);
  406.         try {
  407.             if ($this->userService->isfileExists('apps/uvdesk/custom-fields')) {
  408.                 $customFieldsService $this->customFieldsService;
  409.             } else if ($this->userService->isfileExists('apps/uvdesk/form-component')) {
  410.                 $customFieldsService $this->customFieldsService;
  411.             }
  412.             if (!empty($customFieldsService)) {
  413.                 extract($customFieldsService->customFieldsValidation($request'user'));
  414.             }
  415.         } catch (\Exception $e) {
  416.             // @TODO: Log execption message
  417.         }
  418.         if(!empty($errorFlashMessage)) {
  419.             $this->addFlash('warning'$errorFlashMessage);
  420.         }
  421.         
  422.         $ticketProxy = new CoreFrameworkBundleDataProxies\CreateTicketDataClass();
  423.         $form $this->createForm(CoreFrameworkBundleForms\CreateTicket::class, $ticketProxy);
  424.         // Validate Ticket Details
  425.         $form->submit($requestParams);
  426.         if (false == $form->isSubmitted() || false == $form->isValid()) {
  427.             if (false === $form->isValid()) {
  428.                 // @TODO: We need to handle form errors gracefully.
  429.                 // We should also look into switching to an xhr request instead.
  430.                 // $form->getErrors(true);
  431.             }
  432.             return $this->redirect(!empty($referralURL) ? $referralURL $this->generateUrl('helpdesk_member_ticket_collection'));
  433.         }
  434.         if ('CustomerCreateTicket' === $ticketValidationGroup && !empty($referralTicket)) {
  435.             // Retrieve customer details from referral ticket
  436.             $customer $referralTicket->getCustomer();
  437.             $customerPartialDetails $customer->getCustomerInstance()->getPartialDetails();
  438.         } else if (null != $ticketProxy->getFrom() && null != $ticketProxy->getName()) {
  439.             // Create customer if account does not exists
  440.             $customer $entityManager->getRepository(User::class)->findOneByEmail($ticketProxy->getFrom());
  441.             if (empty($customer) || null == $customer->getCustomerInstance()) {
  442.                 $role $entityManager->getRepository(SupportRole::class)->findOneByCode('ROLE_CUSTOMER');
  443.                 // Create User Instance
  444.                 $customer $this->userService->createUserInstance($ticketProxy->getFrom(), $ticketProxy->getName(), $role, [
  445.                     'source' => 'website',
  446.                     'active' => true
  447.                 ]);
  448.             }
  449.         }
  450.         $ticketData = [
  451.             'from' => $customer->getEmail(),
  452.             'name' => $customer->getFirstName() . ' ' $customer->getLastName(),
  453.             'type' => $ticketProxy->getType(),
  454.             'subject' => $ticketProxy->getSubject(),
  455.             // @TODO: We need to enable support for html messages. 
  456.             // Our focus here instead should be to prevent XSS (filter js)
  457.             'message' => str_replace(['&lt;script&gt;''&lt;/script&gt;'], ''htmlspecialchars($ticketProxy->getReply())),
  458.             'firstName' => $customer->getFirstName(),
  459.             'lastName' => $customer->getLastName(),
  460.             'type' => $ticketProxy->getType(),
  461.             'role' => 4,
  462.             'source' => 'website',
  463.             'threadType' => 'create',
  464.             'createdBy' => 'agent',
  465.             'customer' => $customer,
  466.             'user' => $this->getUser(),
  467.             'attachments' => $request->files->get('attachments'),
  468.         ];
  469.         $thread $this->ticketService->createTicketBase($ticketData);
  470.         if (!empty($thread)) {
  471.             $ticket $thread->getTicket();
  472.         
  473.             if ($request->request->get('customFields') || $request->files->get('customFields')) {
  474.                 $this->ticketService->addTicketCustomFields($thread$request->request->get('customFields'), $request->files->get('customFields'));                        
  475.             }
  476.         // Trigger ticket created event
  477.         try {
  478.             $event = new GenericEvent(CoreWorkflowEvents\Ticket\Create::getId(), [
  479.                 'entity' =>  $thread->getTicket(),
  480.                 'customfields' => $form->getExtraData(),
  481.             ]);
  482.             $this->eventDispatcher->dispatch($event'uvdesk.automation.workflow.execute');
  483.         } catch (\Exception $e) {
  484.             echo '1';// Skip Automation
  485.         }
  486.             $this->addFlash('success'$this->translator->trans('Success ! Ticket has been created successfully.'));
  487.             if ($this->userService->isAccessAuthorized('ROLE_ADMIN')) {
  488.                 return $this->redirect($this->generateUrl('helpdesk_member_ticket', ['ticketId' => $ticket->getId()]));
  489.             }
  490.         } else {
  491.             $this->addFlash('warning'$this->translator->trans('Could not create ticket, invalid details.'));
  492.         }
  493.         return $this->redirect(!empty($referralURL) ? $referralURL $this->generateUrl('helpdesk_member_ticket_collection'));
  494.     }
  495.     public function listTicketTypeCollection(Request $request)
  496.     {
  497.         if (!$this->userService->isAccessAuthorized('ROLE_AGENT_MANAGE_TICKET_TYPE')) {
  498.             return $this->redirect($this->generateUrl('helpdesk_member_dashboard'));
  499.         }
  500.         return $this->render('@UVDeskCoreFramework/ticketTypeList.html.twig');
  501.     }
  502.     public function ticketType(Request $request)
  503.     {
  504.         if (!$this->userService->isAccessAuthorized('ROLE_AGENT_MANAGE_TICKET_TYPE')) {
  505.             return $this->redirect($this->generateUrl('helpdesk_member_dashboard'));
  506.         }
  507.     
  508.         $errorContext = [];
  509.         $em $this->getDoctrine()->getManager();
  510.     
  511.         if ($id $request->attributes->get('ticketTypeId')) {
  512.             $type $em->getRepository(TicketType::class)->find($id);
  513.     
  514.             if (!$type) {
  515.                 $this->noResultFound();
  516.             }
  517.         } else {
  518.             $type = new CoreFrameworkBundleEntities\TicketType();
  519.         }
  520.     
  521.         if ($request->getMethod() == "POST") {
  522.             $data $request->request->all();
  523.             $ticketType $em->getRepository(TicketType::class)->findOneByCode($data['code']);
  524.     
  525.             if (!empty($ticketType) && $id != $ticketType->getId()) {
  526.                 $this->addFlash('warning'sprintf('Error! Ticket type with same name already exists'));
  527.             } else {
  528.                 $type->setCode($data['code']);
  529.                 $type->setDescription($data['description']);
  530.                 $type->setIsActive(isset($data['isActive']) ? 0);
  531.                 
  532.                 // âœ… Set Visibility Value (Default to '2' if not provided)
  533.                 $visibility = isset($data['visibility']) ? (int)$data['visibility'] : 2;
  534.                 $type->setVisibility($visibility);
  535.     
  536.                 $em->persist($type);
  537.                 $em->flush();
  538.     
  539.                 if (!$request->attributes->get('ticketTypeId')) {
  540.                     $this->addFlash('success'$this->translator->trans('Success! Ticket type saved successfully.'));
  541.                 } else {
  542.                     $this->addFlash('success'$this->translator->trans('Success! Ticket type updated successfully.'));
  543.                 }
  544.     
  545.                 return $this->redirect($this->generateUrl('helpdesk_member_ticket_type_collection'));
  546.             }
  547.         }
  548.     
  549.         return $this->render('@UVDeskCoreFramework/ticketTypeAdd.html.twig', [
  550.             'type' => $type,
  551.             'errors' => json_encode($errorContext)
  552.         ]);
  553.     }
  554.     public function listTagCollection(Request $request)
  555.     {
  556.         if (!$this->userService->isAccessAuthorized('ROLE_AGENT_MANAGE_TAG')) {
  557.             return $this->redirect($this->generateUrl('helpdesk_member_dashboard'));
  558.         }
  559.         $enabled_bundles $this->getParameter('kernel.bundles');
  560.         return $this->render('@UVDeskCoreFramework/supportTagList.html.twig', [
  561.             'articlesEnabled' => in_array('UVDeskSupportCenterBundle'array_keys($enabled_bundles)),
  562.         ]);
  563.     }
  564.     public function removeTicketTagXHR($tagIdRequest $request)
  565.     {
  566.         if (!$this->userService->isAccessAuthorized('ROLE_AGENT_MANAGE_TAG')) {
  567.             return $this->redirect($this->generateUrl('helpdesk_member_dashboard'));
  568.         }
  569.         $json = [];
  570.         if($request->getMethod() == "DELETE") {
  571.             $em $this->getDoctrine()->getManager();
  572.             $tag $em->getRepository(Tag::class)->find($tagId);
  573.             if($tag) {
  574.                 $em->remove($tag);
  575.                 $em->flush();
  576.                 $json['alertClass'] = 'success';
  577.                 $json['alertMessage'] = $this->translator->trans('Success ! Tag removed successfully.');
  578.             }
  579.         }
  580.         $response = new Response(json_encode($json));
  581.         $response->headers->set('Content-Type''application/json');
  582.         return $response;
  583.     }
  584.     public function trashTicket(Request $request)
  585.     {
  586.         $ticketId $request->attributes->get('ticketId');
  587.         $entityManager $this->getDoctrine()->getManager();
  588.         $ticket $entityManager->getRepository(CoreBundleTicket::class)->find($ticketId);
  589.         if (!$ticket) {
  590.             $this->noResultFound();
  591.         }
  592.         $user $this->userService->getSessionUser();
  593.         // Proceed only if user has access to the resource
  594.         if (false == $this->ticketService->isTicketAccessGranted($ticket$user)) {
  595.             throw new \Exception('Access Denied'403);
  596.         }
  597.         if (!$ticket->getIsTrashed()) {
  598.             $ticket->setIsTrashed(1);
  599.             $entityManager->persist($ticket);
  600.             $entityManager->flush();
  601.         }
  602.         // Trigger ticket delete event
  603.         $event = new GenericEvent(CoreWorkflowEvents\Ticket\Delete::getId(), [
  604.             'entity' => $ticket,
  605.         ]);
  606.         $this->eventDispatcher->dispatch($event'uvdesk.automation.workflow.execute');
  607.         $this->addFlash('success'$this->translator->trans('Success ! Ticket moved to trash successfully.'));
  608.         return $this->redirectToRoute('helpdesk_member_ticket_collection');
  609.     }
  610.     // Delete a ticket ticket permanently
  611.     public function deleteTicket(Request $request)
  612.     {
  613.         $ticketId $request->attributes->get('ticketId');
  614.         $entityManager $this->getDoctrine()->getManager();
  615.         $ticket $entityManager->getRepository(CoreBundleTicket::class)->find($ticketId);
  616.         if (!$ticket) {
  617.             $this->noResultFound();
  618.         }
  619.         $user $this->userService->getSessionUser();
  620.         // Proceed only if user has access to the resource
  621.         if (false == $this->ticketService->isTicketAccessGranted($ticket$user)) {
  622.             throw new \Exception('Access Denied'403);
  623.         }
  624.         $entityManager->remove($ticket);
  625.         $entityManager->flush();
  626.         $this->addFlash('success'$this->translator->trans('Success ! Success ! Ticket Id #'$ticketId .' has been deleted successfully.'));
  627.         return $this->redirectToRoute('helpdesk_member_ticket_collection');
  628.     }
  629.     public function downloadZipAttachment(Request $request)
  630.     {
  631.         $threadId $request->attributes->get('threadId');
  632.         $attachmentRepository $this->getDoctrine()->getManager()->getRepository(Attachment::class);
  633.         $threadRepository $this->getDoctrine()->getManager()->getRepository(Thread::class);
  634.         $thread $threadRepository->findOneById($threadId);
  635.         $attachment $attachmentRepository->findByThread($threadId);
  636.         if (!$attachment) {
  637.             $this->noResultFound();
  638.         }
  639.         $ticket $thread->getTicket();
  640.         $user $this->userService->getSessionUser();
  641.         
  642.         // Proceed only if user has access to the resource
  643.         if (false == $this->ticketService->isTicketAccessGranted($ticket$user)) {
  644.             throw new \Exception('Access Denied'403);
  645.         }
  646.         $zipname 'attachments/' .$threadId.'.zip';
  647.         $zip = new \ZipArchive;
  648.         $zip->open($zipname\ZipArchive::CREATE);
  649.         if (count($attachment)) {
  650.             foreach ($attachment as $attach) {
  651.                 $zip->addFile(substr($attach->getPath(), 1));
  652.             }
  653.         }
  654.         $zip->close();
  655.         $response = new Response();
  656.         $response->setStatusCode(200);
  657.         $response->headers->set('Content-type''application/zip');
  658.         $response->headers->set('Content-Disposition''attachment; filename=' $threadId '.zip');
  659.         $response->sendHeaders();
  660.         $response->setContent(readfile($zipname));
  661.         return $response;
  662.     }
  663.     public function downloadAttachment(Request $request)
  664.     {   
  665.         $attachmentId $request->attributes->get('attachmendId');
  666.         $attachmentRepository $this->getDoctrine()->getManager()->getRepository(Attachment::class);
  667.         $attachment $attachmentRepository->findOneById($attachmentId);
  668.         $baseurl $request->getScheme() . '://' $request->getHttpHost() . $request->getBasePath();
  669.         if (!$attachment) {
  670.             $this->noResultFound();
  671.         }
  672.         $ticket $attachment->getThread()->getTicket();
  673.         $user $this->userService->getSessionUser();
  674.         
  675.         // Proceed only if user has access to the resource
  676.         if (false == $this->ticketService->isTicketAccessGranted($ticket$user)) {
  677.             throw new \Exception('Access Denied'403);
  678.         }
  679.         $path $this->kernel->getProjectDir() . "/public/"$attachment->getPath();
  680.         $response = new Response();
  681.         $response->setStatusCode(200);
  682.         $response->headers->set('Content-type'$attachment->getContentType());
  683.         $response->headers->set('Content-Disposition''attachment; filename='$attachment->getName());
  684.         $response->headers->set('Content-Length'$attachment->getSize());
  685.         $response->sendHeaders();
  686.         $response->setContent(readfile($path));
  687.         return $response;
  688.     }
  689.     /**
  690.      * If customer is playing with url and no result is found then what will happen
  691.      * @return 
  692.      */
  693.     protected function noResultFound()
  694.     {
  695.         throw new NotFoundHttpException('Not Found!');
  696.     }
  697. }