diff --git a/API/Threads.php b/API/Threads.php new file mode 100644 index 0000000..5d8d03f --- /dev/null +++ b/API/Threads.php @@ -0,0 +1,170 @@ +request->all()? : json_decode($request->getContent(),true); + + if (!isset($data['threadType']) || !isset($data['message'])) { + $json['error'] = 'missing fields'; + $json['description'] = 'required: threadType: reply|forward|note , message'; + return new JsonResponse($json, Response::HTTP_BAD_REQUEST); + } + + $ticket = $this->getDoctrine()->getRepository('UVDeskCoreFrameworkBundle:Ticket')->findOneById($ticketid); + + // Check for empty ticket + if (empty($ticket)) { + $json['error'] = "Error! No such ticket with ticket id exist"; + return new JsonResponse($json, Response::HTTP_NOT_FOUND); + } else if ('POST' != $request->getMethod()) { + $json['error'] = "Error! invalid request method"; + return new JsonResponse($json, Response::HTTP_BAD_REQUEST); + } + + // Check if message content is empty + $parsedMessage = trim(strip_tags($data['message'], '')); + $parsedMessage = str_replace(' ', '', $parsedMessage); + $parsedMessage = str_replace(' ', '', $parsedMessage); + + if (null == $parsedMessage) { + $json['error'] = "Warning ! Reply content cannot be left blank."; + return new JsonResponse($json, Response::HTTP_BAD_REQUEST); + } + + if (array_key_exists('actAsType', $data) && isset($data['actAsEmail'])) { + $actAsType = strtolower($data['actAsType']); + $actAsEmail = $data['actAsEmail']; + if ($actAsType == 'customer') { + $customer = $this->getDoctrine()->getRepository('UVDeskCoreFrameworkBundle:User')->findOneByEmail($data['actAsEmail']); + } else if($actAsType == 'agent' ) { + $user = $this->getDoctrine()->getRepository('UVDeskCoreFrameworkBundle:User')->findOneByEmail($data['actAsEmail']); + } else { + $json['error'] = 'Error! invalid actAs details.'; + $json['description'] = 'possible values actAsType: customer,agent. Also provide actAsEmail parameter with actAsType agent.'; + return new JsonResponse($json, Response::HTTP_BAD_REQUEST); + } + if (!$user) { + $json['error'] = 'Error! invalid actAs details.'; + return new JsonResponse($json, Response::HTTP_BAD_REQUEST); + } + } + + if ($actAsType == 'agent') { + $data['user'] = isset($user) && $user ? $user : $this->get('user.service')->getCurrentUser(); + } else { + $data['user'] = $customer; + } + + $threadDetails = [ + 'user' => $data['user'], + 'createdBy' => $actAsType, + 'source' => 'api', + 'threadType' => strtolower($data['threadType']), + 'message' => str_replace(['<script>', '</script>'], '', $data['message']), + 'attachments' => $request->files->get('attachments') + ]; + + if (!empty($data['status'])){ + $ticketStatus = $this->getDoctrine()->getRepository('UVDeskCoreFrameworkBundle:TicketStatus')->findOneByCode($data['status']); + $ticket->setStatus($ticketStatus); + } + if (isset($data['to'])) { + $threadDetails['to'] = $data['to']; + } + + if (isset($data['cc'])) { + $threadDetails['cc'] = $data['cc']; + } + + if (isset($data['cccol'])) { + $threadDetails['cccol'] = $data['cccol']; + } + + if (isset($data['bcc'])) { + $threadDetails['bcc'] = $data['bcc']; + } + + // Create Thread + $thread = $this->get('ticket.service')->createThread($ticket, $threadDetails); + + // Check for thread types + switch ($thread->getThreadType()) { + case 'note': + $event = new GenericEvent(CoreWorkflowEvents\Ticket\Note::getId(), [ + 'entity' => $ticket, + 'thread' => $thread + ]); + $this->get('event_dispatcher')->dispatch('uvdesk.automation.workflow.execute', $event); + + $json['success'] = "success', Note added to ticket successfully."; + return new JsonResponse($json, Response::HTTP_OK); + break; + case 'reply': + $event = new GenericEvent(CoreWorkflowEvents\Ticket\AgentReply::getId(), [ + 'entity' => $ticket, + 'thread' => $thread + ]); + $this->get('event_dispatcher')->dispatch('uvdesk.automation.workflow.execute', $event); + + $json['success'] = "success', Reply added to ticket successfully.."; + return new JsonResponse($json, Response::HTTP_OK); + break; + case 'forward': + // Prepare headers + $headers = ['References' => $ticket->getReferenceIds()]; + + if (null != $ticket->currentThread->getMessageId()) { + $headers['In-Reply-To'] = $ticket->currentThread->getMessageId(); + } + + // Prepare attachments + $attachments = $entityManager->getRepository(Attachment::class)->findByThread($thread); + + $projectDir = $this->get('kernel')->getProjectDir(); + $attachments = array_map(function($attachment) use ($projectDir) { + return str_replace('//', '/', $projectDir . "/public" . $attachment->getPath()); + }, $attachments); + + // Forward thread to users + try { + $messageId = $this->get('email.service')->sendMail($params['subject'] ?? ("Forward: " . $ticket->getSubject()), $thread->getMessage(), $thread->getReplyTo(), $headers, $ticket->getMailboxEmail(), $attachments ?? [], $thread->getCc() ?: [], $thread->getBcc() ?: []); + + if (!empty($messageId)) { + $thread->setMessageId($messageId); + + $entityManager->persist($createdThread); + $entityManager->flush(); + } + } catch (\Exception $e) { + // Do nothing ... + // @TODO: Log exception + } + + $json['success'] = "success', Reply added to the ticket and forwarded successfully."; + return new JsonResponse($json, Response::HTTP_OK); + break; + default: + break; + } + } +} diff --git a/API/Tickets.php b/API/Tickets.php index 9503905..40ebed9 100644 --- a/API/Tickets.php +++ b/API/Tickets.php @@ -10,6 +10,11 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Webkul\UVDesk\CoreFrameworkBundle\Workflow\Events as CoreWorkflowEvents; +use Symfony\Component\Serializer\Serializer; +use Symfony\Component\Serializer\Encoder\XmlEncoder; +use Symfony\Component\Serializer\Encoder\JsonEncoder; +use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; + class Tickets extends Controller { /** @@ -20,17 +25,17 @@ class Tickets extends Controller public function fetchTickets(Request $request) { $json = []; + $entityManager = $this->getDoctrine()->getManager(); $ticketRepository = $this->getDoctrine()->getRepository('UVDeskCoreFrameworkBundle:Ticket'); $userRepository = $this->getDoctrine()->getRepository('UVDeskCoreFrameworkBundle:User'); - $em = $this->getDoctrine()->getManager(); - - if($request->query->get('actAsType')) { + if ($request->query->get('actAsType')) { switch($request->query->get('actAsType')) { case 'customer': - $user = $this->getUser(); - if($user) { - $json = $repository->getAllCustomerTickets($request->query, $this->container, $user); + $customer = $entityManager->getRepository('UVDeskCoreFrameworkBundle:User')->findOneByEmail($data['actAsEmail']); + + if ($customer) { + $json = $repository->getAllCustomerTickets($request->query, $this->container, $customer); } else { $json['error'] = 'Error! Resource not found.'; return new JsonResponse($json, Response::HTTP_NOT_FOUND); @@ -38,8 +43,9 @@ public function fetchTickets(Request $request) return new JsonResponse($json); break; case 'agent': - $user = $this->getUser(); - if($user) { + $user = $entityManager->getRepository('UVDeskCoreFrameworkBundle:User')->findOneByEmail($data['actAsEmail']); + + if ($user) { $request->query->set('agent', $user->getId()); } else { $json['error'] = 'Error! Resource not found.'; @@ -49,7 +55,6 @@ public function fetchTickets(Request $request) default: $json['error'] = 'Error! invalid actAs details.'; return new JsonResponse($json, Response::HTTP_BAD_REQUEST); - break; } } @@ -59,6 +64,7 @@ public function fetchTickets(Request $request) 'user' => $this->getUser()->getId(), 'name' => $this->getUser()->getFirstName().' '.$this->getUser()->getLastname(), ]; + $json['agents'] = $this->get('user.service')->getAgentsPartialDetails(); $json['status'] = $this->get('ticket.service')->getStatus(); $json['group'] = $userRepository->getSupportGroups(); @@ -86,8 +92,258 @@ public function fetchTicketsMetadata(Request $request) * @param Request $request * @return void */ - public function trashTickets(Request $request) + public function trashTicket(Request $request) { - return new JsonResponse([]); + $ticketId = $request->attributes->get('ticketId'); + $entityManager = $this->getDoctrine()->getManager(); + $ticket = $entityManager->getRepository('UVDeskCoreFrameworkBundle:Ticket')->find($ticketId); + + if (!$ticket) { + $this->noResultFound(); + } + + if (!$ticket->getIsTrashed()) { + $ticket->setIsTrashed(1); + $entityManager->persist($ticket); + $entityManager->flush(); + + $json['success'] = 'Success ! Ticket moved to trash successfully.'; + $statusCode = Response::HTTP_OK; + + // Trigger ticket delete event + $event = new GenericEvent(CoreWorkflowEvents\Ticket\Delete::getId(), [ + 'entity' => $ticket, + ]); + + $this->get('event_dispatcher')->dispatch('uvdesk.automation.workflow.execute', $event); + } else { + $json['error'] = 'Warning ! Ticket is already in trash.'; + $statusCode = Response::HTTP_BAD_REQUEST; + } + + return new JsonResponse($json, $statusCode); + } + + /** + * Create support tickets. + * + * @param Request $request + * @return void + */ + public function createTicket(Request $request) + { + $data = $request->request->all()? : json_decode($request->getContent(),true); + foreach($data as $key => $value) { + if(!in_array($key, ['subject', 'group', 'type', 'status','locale','domain', 'priority', 'agent', 'replies', 'createdAt', 'updatedAt', 'customFields', 'files', 'from', 'name', 'message', 'tags', 'actAsType', 'actAsEmail'])) { + unset($data[$key]); + } + } + + if(!(isset($data['from']) && isset($data['name']) && isset($data['subject']) && isset($data['message']) && isset($data['actAsType']) || isset($data['actAsEmail']) )) { + $json['error'] = 'required fields: name ,from, message, actAsType or actAsEmail'; + return new JsonResponse($json, Response::HTTP_BAD_REQUEST); + } + + if($data) { + $error = false; + $message = ''; + $entityManager = $this->getDoctrine()->getManager(); + + if ($data['subject'] == '') { + $message = "Warning! Please complete subject field value!"; + $statusCode = Response::HTTP_BAD_REQUEST; + } elseif($data['message'] == '') { + $json['message'] = "Warning! Please complete message field value!"; + $statusCode = Response::HTTP_BAD_REQUEST; + } elseif(filter_var($data['from'], FILTER_VALIDATE_EMAIL) === false) { + $json['message'] = "Warning! Invalid from Email Address!"; + $statusCode = Response::HTTP_BAD_REQUEST; + } + elseif ($data['actAsType'] == '' && $data['actAsEmail'] == '') { + $json['message'] = "Warning! Provide atleast one parameter actAsType(agent or customer) or actAsEmail"; + $statusCode = Response::HTTP_BAD_REQUEST; + } + + if (!$error) { + $name = explode(' ',$data['name']); + $ticketData['firstName'] = $name[0]; + $ticketData['lastName'] = isset($name[1]) ? $name[1] : ''; + $ticketData['role'] = 4; + + if ((array_key_exists('actAsType', $data)) && strtolower($data['actAsType']) == 'customer') { + $actAsType = strtolower($data['actAsType']); + } else if((array_key_exists('actAsEmail', $data)) && strtolower($data['actAsType']) == 'agent') { + $user = $entityManager->getRepository('UVDeskCoreFrameworkBundle:User')->findOneByEmail($data['actAsEmail']); + + if ($user) { + $actAsType = 'agent'; + } else { + $json['error'] = "Error ! actAsEmail is not valid"; + return new JsonResponse($json, Response::HTTP_BAD_REQUEST); + } + } else { + $json['warning'] = 'Warning ! For Customer spacify actAsType as customer and for Agent spacify both parameter actASType as agent and actAsEmail as agent email'; + $statusCode = Response::HTTP_BAD_REQUEST; + return new JsonResponse($json, $statusCode); + } + + // Create customer if account does not exists + $customer = $entityManager->getRepository('UVDeskCoreFrameworkBundle:User')->findOneByEmail($data['from']); + + if (empty($customer) || null == $customer->getCustomerInstance()) { + $role = $entityManager->getRepository('UVDeskCoreFrameworkBundle:SupportRole')->findOneByCode('ROLE_CUSTOMER'); + + // Create User Instance + $customer = $this->get('user.service')->createUserInstance($data['from'], $data['name'], $role, [ + 'source' => 'api', + 'active' => true + ]); + } + + if ($actAsType == 'agent') { + $data['user'] = isset($user) && $user ? $user : $this->get('user.service')->getCurrentUser(); + } else { + $data['user'] = $customer; + } + + $ticketData['user'] = $data['user']; + $ticketData['subject'] = $data['subject']; + $ticketData['message'] = $data['message']; + $ticketData['customer'] = $customer; + $ticketData['source'] = 'api'; + $ticketData['threadType'] = 'create'; + $ticketData['createdBy'] = $actAsType; + $ticketData['attachments'] = $request->files->get('attachments'); + + $extraKeys = ['tags', 'group', 'priority', 'status', 'agent', 'createdAt', 'updatedAt']; + + $requestData = $data; + foreach ($extraKeys as $key) { + if (isset($ticketData[$key])) { + unset($ticketData[$key]); + } + } + + $thread = $this->get('ticket.service')->createTicketBase($ticketData); + // Trigger ticket created event + try { + $event = new GenericEvent(CoreWorkflowEvents\Ticket\Create::getId(), [ + 'entity' => $thread->getTicket(), + ]); + $this->get('event_dispatcher')->dispatch('uvdesk.automation.workflow.execute', $event); + } catch (\Exception $e) { + // + } + + $json['message'] = 'Success ! Ticket has been created successfully.'; + $json['ticketId'] = $thread->getTicket()->getId(); + $statusCode = Response::HTTP_OK; + + } else { + $json['message'] = 'Warning ! Required parameters should not be blank'; + $statusCode = Response::HTTP_BAD_REQUEST; + } + } else { + $json['error'] = 'invalid/empty size of Request'; + $json['message'] = 'Warning ! Post size can not exceed 25MB'; + $statusCode = Response::HTTP_BAD_REQUEST; + } + + return new JsonResponse($json, $statusCode); + } + + /** + * View support tickets. + * + * @param Request $request + * @return void + */ + public function viewTicket($ticketId, Request $request) + { + $entityManager = $this->getDoctrine()->getManager(); + $userRepository = $entityManager->getRepository('UVDeskCoreFrameworkBundle:User'); + $ticketRepository = $entityManager->getRepository('UVDeskCoreFrameworkBundle:Ticket'); + + $ticket = $ticketRepository->findOneById($ticketId); + + if (empty($ticket)) { + throw new \Exception('Page not found'); + } + + $agent = $ticket->getAgent(); + $customer = $ticket->getCustomer(); + + // Mark as viewed by agents + if (false == $ticket->getIsAgentViewed()) { + $ticket->setIsAgentViewed(true); + + $entityManager->persist($ticket); + $entityManager->flush(); + } + + // Ticket status Collection + $status = array_map(function ($statusCollection) { + return [ + 'id' => $statusCollection->getId(), + 'code' => $statusCollection->getCode(), + 'colorCode' => $statusCollection->getColorCode(), + 'description' => $statusCollection->getDescription(), + ]; + }, $entityManager->getRepository('UVDeskCoreFrameworkBundle:TicketStatus')->findAll()); + + // Ticket Type Collection + $type = array_map(function ($ticketTypeCollection) { + return [ + 'id' => $ticketTypeCollection->getId(), + 'code' => $ticketTypeCollection->getCode(), + 'isActive' => $ticketTypeCollection->getIsActive(), + 'description' => $ticketTypeCollection->getDescription(), + ]; + }, $entityManager->getRepository('UVDeskCoreFrameworkBundle:TicketType')->findByIsActive(true)); + + // Priority Collection + $priority = array_map(function ($ticketPriorityCollection) { + return [ + 'id' => $ticketPriorityCollection->getId(), + 'code' => $ticketPriorityCollection->getCode(), + 'colorCode' => $ticketPriorityCollection->getColorCode(), + 'description' => $ticketPriorityCollection->getDescription(), + ]; + }, $entityManager->getRepository('UVDeskCoreFrameworkBundle:TicketPriority')->findAll()); + + $ticketObj = $ticket; + $ticket = json_decode($this->objectSerializer($ticketObj), true); + + return new JsonResponse([ + 'ticket' => $ticket, + 'totalCustomerTickets' => ($ticketRepository->countCustomerTotalTickets($customer)), + 'ticketAgent' => !empty($agent) ? $agent->getAgentInstance()->getPartialDetails() : null, + 'customer' => $customer->getCustomerInstance()->getPartialDetails(), + 'supportGroupCollection' => $userRepository->getSupportGroups(), + 'supportTeamCollection' => $userRepository->getSupportTeams(), + 'ticketStatusCollection' => $status, + 'ticketPriorityCollection' => $priority, + 'ticketTypeCollection' => $type + ]); + } + + /** + * objectSerializer This function convert Entity object into json contenxt + * @param Object $object Customer Entity object + * @return JSON JSON context + */ + public function objectSerializer($object) { + $object->formatedCreatedAt = new \Datetime; + $encoders = array(new XmlEncoder(), new JsonEncoder()); + $normalizer = new ObjectNormalizer(); + $normalizer->setCircularReferenceHandler(function ($object) { + return $object->getId(); + }); + + $normalizers = array($normalizer); + $serializer = new Serializer($normalizers, $encoders); + $jsonContent = $serializer->serialize($object, 'json'); + + return $jsonContent; } } diff --git a/CHANGELOG-1.0.md b/CHANGELOG-1.0.md index a4648e0..3665647 100644 --- a/CHANGELOG-1.0.md +++ b/CHANGELOG-1.0.md @@ -1,6 +1,10 @@ CHANGELOG for 1.0.x =================== +* 1.0.1 (Released on 2020-02-24) + + * **Ticket Related APIs:** Added Ticket Related APIs like Ticket Create, Fetch all tickets, View or load a ticket, Trash ticket, Ticket reply(Add a rply to ticket). + * 1.0.0 (Released on 2020-01-21) * **Access Token Generate:** Can generate multiple token from member panel and use it for API request. diff --git a/README.md b/README.md index e858702..f914c0b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,11 @@ To require the api bundle into your symfony project, simply run the following fr ```bash $ composer require uvdesk/api-bundle ``` +after installing api bundle run the below command: +```bash +$ php bin/console doctrine:schema:update --force +``` License -------------- diff --git a/Resources/config/routes/api/v1.0/threads.yaml b/Resources/config/routes/api/v1.0/threads.yaml index e9acc92..0cedb36 100644 --- a/Resources/config/routes/api/v1.0/threads.yaml +++ b/Resources/config/routes/api/v1.0/threads.yaml @@ -1 +1,7 @@ # Enter ticket thread api endpoints here ... +uvdesk_api_bundle_api_tickets_v1.0_add_thread: + path: /ticket/{ticketid}/thread + controller: Webkul\UVDesk\ApiBundle\API\Threads::saveThread + methods: [POST] + requirements: + ticketid: '\d+' \ No newline at end of file diff --git a/Resources/config/routes/api/v1.0/tickets.yaml b/Resources/config/routes/api/v1.0/tickets.yaml index fbbdc84..d9cfb7d 100644 --- a/Resources/config/routes/api/v1.0/tickets.yaml +++ b/Resources/config/routes/api/v1.0/tickets.yaml @@ -8,7 +8,19 @@ uvdesk_api_bundle_api_tickets_v1.0_load_ticket_attributes: controller: Webkul\UVDesk\ApiBundle\API\Tickets::fetchTicketsMetadata methods: [GET] -uvdesk_api_bundle_api_tickets_v1.0_trash_tickets: - path: /tickets/trash - controller: Webkul\UVDesk\ApiBundle\API\Tickets::trashTickets +uvdesk_api_bundle_api_tickets_v1.0_trash_ticket: + path: /trash/ticket/{ticketId} + controller: Webkul\UVDesk\ApiBundle\API\Tickets::trashTicket methods: [PUT] + requirements: + ticketId: '\d+' + +uvdesk_api_bundle_api_tickets_v1.0_create_ticket: + path: /ticket + controller: Webkul\UVDesk\ApiBundle\API\Tickets::createTicket + methods: [POST] + +uvdesk_api_bundle_api_tickets_v1.0_view_ticket: + path: /ticket/{ticketId} + controller: Webkul\UVDesk\ApiBundle\API\Tickets::viewTicket + methods: [GET] \ No newline at end of file