PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 1.5.4
Fluent Support – Helpdesk & Customer Support Ticket System v1.5.4
2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 All 68 releases
fluent-support / app / Http / Controllers / TicketController.php

TicketController.php in Fluent Support – Helpdesk & Customer Support Ticket System 1.5.4, at app/Http/Controllers/TicketController.php

720 lines 23.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentSupport\App\Http\Controllers;
4
5 use FluentSupport\App\Models\Agent;
6 use FluentSupport\App\Models\Attachment;
7 use FluentSupport\App\Models\Customer;
8 use FluentSupport\App\Models\MailBox;
9 use FluentSupport\App\Models\Conversation;
10 use FluentSupport\App\Models\Product;
11 use FluentSupport\App\Models\Ticket;
12 use FluentSupport\App\Modules\PermissionManager;
13 use FluentSupport\App\Services\EmailNotification\Settings;
14 use FluentSupport\App\Services\Helper;
15 use FluentSupport\App\Services\ProfileInfoService;
16 use FluentSupport\App\Services\TicketHelper;
17 use FluentSupport\App\Services\TicketQueryService;
18 use FluentSupport\App\Services\Tickets\ResponseService;
19 use FluentSupport\App\Services\Tickets\TicketService;
20 use FluentSupport\Framework\Request\Request;
21
22 class TicketController extends Controller
23 {
24 public function me(Request $request)
25 {
26 $user = wp_get_current_user();
27
28 $settings = [
29 'user_id' => $user->id,
30 'email' => $user->user_email,
31 'person' => Helper::getAgentByUserId($user->ID),
32 'permissions' => PermissionManager::currentUserPermissions(),
33 'request' => $request->all()
34 ];
35
36 if ($request->get('with_portal_settings')) {
37
38 $mimeHeadings = Helper::getAcceptedMimeHeadings();
39 $businessSettings = (new Settings())->globalBusinessSettings();
40 $maxFileSize = absint($businessSettings['max_file_size']);
41
42 $portalSettings = [
43 'support_products' => Product::select(['id', 'title'])->get(),
44 'customer_ticket_priorities' => Helper::customerTicketPriorities(),
45 'has_file_upload' => !!Helper::ticketAcceptedFileMiles(),
46 'has_rich_text_editor' => true,
47 'max_file_size' => $maxFileSize,
48 'mime_headings' => $mimeHeadings
49 ];
50
51 $portalSettings = apply_filters('fluent_support/customer_portal_vars', $portalSettings);
52
53 $settings['portal_settings'] = $portalSettings;
54 }
55
56
57 return $settings;
58 }
59
60 public function index(Request $request)
61 {
62
63 $filterType = $request->get('filter_type', 'simple');
64
65 $queryArgs = [
66 'with' => [],
67 'filter_type' => $filterType,
68 'sort_by' => $request->get('order_by', 'id'),
69 'sort_type' => $request->get('order_type', 'DESC'),
70 ];
71
72 if($request->get('filter_type')=='advanced'){
73 $queryArgs['filters_groups_raw'] = json_decode($this->request->get('advanced_filters'), true);
74 } else {
75 $queryArgs['simple_filters'] = $request->get('filters', []);
76 $queryArgs['search'] = trim(sanitize_text_field($request->get('search', '')));
77 if ($customerId = $request->get('customer_id')) {
78 $queryArgs['customer_id'] = intval($customerId);
79 }
80 }
81
82 $ticketsModel = (new TicketQueryService($queryArgs))->getModel();
83
84 $ticketsModel = $ticketsModel->with([
85 'customer' => function ($query) {
86 $query->select(['first_name', 'last_name', 'email', 'id', 'avatar']);
87 }, 'agent' => function ($query) {
88 $query->select(['first_name', 'last_name', 'id']);
89 },
90 'product',
91 'tags',
92 'preview_response' => function ($query) {
93 $query->orderBy('id', 'desc');
94 }
95 ]);
96
97
98 // apply filters by access level
99 do_action_ref_array('fluent_support/tickets_query_by_permission_ref', [&$ticketsModel, false]);
100
101
102 $tickets = $ticketsModel->paginate();
103
104 $perPage = $request->get('per_page');
105
106 foreach ($tickets as $ticket) {
107 if ($perPage < 15) {
108 if ($ticket->status != 'closed') {
109 $ticket->live_activity = TicketHelper::getActivity($ticket->id);
110 } else {
111 $ticket->live_activity = [];
112 }
113 }
114 }
115
116 return [
117 'tickets' => $tickets
118 ];
119 }
120
121 public function createTicket(Request $request)
122 {
123 $ticketData = $request->get('ticket', []);
124 $maybeNewCustomer = $request->get('newCustomer');
125
126 if ($ticketData['create_wp_user'] == 'yes'){
127 if(!username_exists($maybeNewCustomer['username'])){
128 $authController = new AuthController();
129 $createdUser = $authController->createUser($maybeNewCustomer);
130 $authController->maybeUpdateUser($createdUser, $maybeNewCustomer);
131 }else{
132 return $this->sendError(__('This username is already exist in WordPress', 'fluent-support'));
133 }
134 }
135
136 if($ticketData['create_customer'] == 'yes'){
137 if (!empty($maybeNewCustomer) && is_null(Customer::where('email', $maybeNewCustomer['email'])->first())){
138 $createCustomer = Customer::create($maybeNewCustomer);
139 if ($createCustomer){
140 $ticketData['customer_id'] = $createCustomer->id;
141 }
142 }
143 else{
144 return $this->sendError(__('Customer with this email already exist', 'fluent-support'));
145 }
146 }
147
148 $this->validate($ticketData, [
149 'customer_id' => 'required',
150 'title' => 'required',
151 'content' => 'required'
152 ]);
153
154 $customer = Customer::findOrFail($ticketData['customer_id']);
155
156 if (empty($ticketData['mailbox_id'])) {
157 $mailbox = Helper::getDefaultMailBox();
158 $ticketData['mailbox_id'] = $mailbox->id;
159 } else {
160 $mailbox = MailBox::findOrFail($ticketData['mailbox_id']); // just for validation
161 }
162
163 if (!empty($ticketData['product_id'])) {
164 $data['product_source'] = 'local';
165 }
166
167 $ticketData['title'] = sanitize_text_field(wp_unslash($ticketData['title']));
168
169 $ticketData['content'] = wp_unslash(wp_kses_post($ticketData['content']));
170
171 if (!empty($ticketData['priority'])) {
172 $ticketData['priority'] = sanitize_text_field($ticketData['priority']);
173 }
174
175 $ticketData['client_priority'] = sanitize_text_field($ticketData['client_priority']);
176
177 $ticketData = apply_filters('fluent_support/create_ticket_data', $ticketData, $customer);
178 do_action('fluent_support/before_ticket_create', $ticketData, $customer);
179
180 $createdTicket = Ticket::create($ticketData);
181
182 if (defined('FLUENTSUPPORTPRO') && !empty($ticketData['custom_fields'])) {
183 $createdTicket->syncCustomFields($ticketData['custom_fields']);
184 $createdTicket->custom_fields = $createdTicket->customData();
185 }
186
187 do_action('fluent_support/ticket_created', $createdTicket, $customer);
188
189 return [
190 'message' => __('Ticket has been created successfully', 'fluent-support'),
191 'ticket' => $createdTicket
192 ];
193
194 }
195
196 public function getTicket(Request $request, $ticketId)
197 {
198 $agent = Helper::getAgentByUserId();
199 $ticketWith = $request->get('with', ['customer', 'agent', 'product', 'mailbox', 'tags', 'attachments' => function ($q) {
200 $q->whereIn('status', ['active', 'inline']);
201 }]);
202 $responseWith = $request->get('response_with', ['person', 'attachments']);
203
204 $ticket = Ticket::with($ticketWith)
205 ->findOrFail($ticketId);
206
207 if ($ticket->customer) {
208 $ticket->customer->profile_edit_url = $ticket->customer->getUserProfileEditUrl();
209 }
210
211 if (!PermissionManager::hasTicketPermission($ticket)) {
212 return $this->sendError([
213 'message' => __('Sorry, You do not have permission to this ticket', 'fluent-support')
214 ]);
215 }
216
217
218 if ($ticket->status == 'closed') {
219 $ticket->load('closed_by_person');
220 }
221
222 $responses = Conversation::where('ticket_id', $ticketId)
223 ->with($responseWith)
224 ->orderBy('id', 'DESC')
225 ->get();
226
227 foreach ($responses as $response) {
228 $response->content = make_clickable(wpautop($response->content, false));
229 }
230
231 $ticket->content = make_clickable(wpautop($ticket->content, false));
232
233 $ticket->live_activity = TicketHelper::getActivity($ticketId, $agent->id);
234
235 if (defined('FLUENTSUPPORTPRO')) {
236 $ticket->custom_fields = $ticket->customData('admin', true);
237 }
238
239 $data = [
240 'ticket' => $ticket,
241 'responses' => $responses,
242 'agent_id' => $agent->id
243 ];
244
245 if (in_array('fluentcrm_profile', $request->get('with_data', [])) && defined('FLUENTCRM')) {
246 $data['fluentcrm_profile'] = Helper::getFluentCrmContactData($ticket->customer);
247 }
248
249 return $data;
250
251 }
252
253 public function createResponse(Request $request, $ticketId)
254 {
255 $data = $request->all();
256
257 $this->validate($data, [
258 'content' => 'required'
259 ]);
260
261 $agent = Helper::getAgentByUserId(get_current_user_id());
262
263 if (!$agent) {
264 return $this->sendError([
265 'message' => __('Sorry, You do not have permission. Please add yourself as support agent first', 'fluent-support')
266 ]);
267 }
268
269 $ticket = Ticket::findOrFail($ticketId);
270
271 if (!PermissionManager::hasTicketPermission($ticket)) {
272 return $this->sendError([
273 'message' => __('Sorry, You do not have permission to this ticket', 'fluent-support')
274 ]);
275 }
276
277 $responseData = (new ResponseService())->createResponse($data, $agent, $ticket);
278
279 $responseData['response']->content = make_clickable(wpautop($responseData['response']->content, false));
280
281 return [
282 'message' => __('Response has been added'),
283 'response' => $responseData['response'],
284 'ticket' => $responseData['ticket'],
285 'update_data' => $responseData['update_data']
286 ];
287 }
288
289 public function getTicketWidgets(Request $request, $ticketId)
290 {
291 $ticket = Ticket::with('customer')->findOrFail($ticketId);
292
293 if (!PermissionManager::hasTicketPermission($ticket)) {
294 return $this->sendError([
295 'message' => __('Sorry, You do not have permission to this ticket', 'fluent-support')
296 ]);
297 }
298
299 $otherTickets = Ticket::where('id', '!=', $ticketId)
300 ->select(['id', 'title', 'status', 'created_at'])
301 ->where('customer_id', $ticket->customer_id)
302 ->orderBy('id', 'DESC')
303 ->limit(10)
304 ->get();
305
306 return [
307 'other_tickets' => $otherTickets,
308 'extra_widgets' => ProfileInfoService::getProfileExtraWidgets($ticket->customer)
309 ];
310 }
311
312 public function updateTicketProperty(Request $request, $ticketId)
313 {
314 $assigner = Helper::getAgentByUserId(get_current_user_id());
315 $ticket = Ticket::findOrFail($ticketId);
316 $propName = $request->get('prop_name');
317 $propValue = $request->get('prop_value');
318
319 if (!PermissionManager::hasTicketPermission($ticket)) {
320 return $this->sendError([
321 'message' => __('Sorry, You do not have permission to this ticket', 'fluent-support')
322 ]);
323 }
324
325 $prevValue = $ticket->{$propName};
326 if ($propName && $propValue && $prevValue != $propValue) {
327 $ticket->{$propName} = $propValue;
328 $ticket->save();
329 }
330
331 $updateData = [];
332
333 if ($propName == 'product_id') {
334 $ticket->load('product');
335 $updateData['product'] = $ticket->product;
336 } else if ($propName == 'agent_id') {
337 $ticket->load('agent');
338 $updateData['agent'] = $ticket->agent;
339 $updateData['assigner'] = (new TicketService())->onAgentChange($ticket, $assigner);
340 if ($prevValue != $ticket->{$propName}) {
341 do_action('fluent_support/agent_assigned_to_ticket', $ticket->agent, $ticket);
342 }
343 }
344
345 return [
346 'message' => __(str_replace('_', ' ', ucwords($propName)) . ' has been updated', 'fluent-support'),
347 'update_data' => $updateData
348 ];
349 }
350
351 public function closeTicket(Request $request, $ticketId)
352 {
353 $agent = Helper::getAgentByUserId(get_current_user_id());
354
355 $ticket = Ticket::findOrFail($ticketId);
356
357 if (!PermissionManager::hasTicketPermission($ticket)) {
358 return $this->sendError([
359 'message' => __('Sorry, You do not have permission to this ticket', 'fluent-support')
360 ]);
361 }
362
363 return [
364 'message' => __('Ticket has been closed', 'fluent_support'),
365 'ticket' => (new TicketService())->close($ticket, $agent)
366 ];
367 }
368
369 public function reOpenTicket(Request $request, $ticketId)
370 {
371 $agent = Helper::getAgentByUserId(get_current_user_id());
372
373 $ticket = Ticket::findOrFail($ticketId);
374
375 if (!PermissionManager::hasTicketPermission($ticket)) {
376 return $this->sendError([
377 'message' => __('Sorry, You do not have permission to this ticket', 'fluent-support')
378 ]);
379 }
380
381 return [
382 'message' => __('Ticket has been opened again', 'fluent_support'),
383 'ticket' => (new TicketService())->reopen($ticket, $agent)
384 ];
385 }
386
387 public function doBulkActions(Request $request)
388 {
389 $ticketIds = $request->get('ticket_ids', []);
390 $action = $request->get('bulk_action');
391 $hasAllPermission = PermissionManager::currentUserCan('fst_manage_other_tickets');
392 $agent = Helper::getAgentByUserId();
393 $query = Ticket::whereIn('id', $ticketIds);
394
395 if (!$hasAllPermission) {
396 $query->where('agent_id', $agent->id);
397 }
398
399 if ($action == 'close_tickets') {
400 $query->where('status', '!=', 'closed');
401 $tickets = $query->get();
402 foreach ($tickets as $ticket) {
403 (new TicketService())->close($ticket, $agent);
404 }
405
406 return [
407 'message' => sprintf(__('%d tickets have been closed', 'fluent-support'), count($tickets))
408 ];
409 } else if ($action == 'delete_tickets') {
410 $tickets = $query->get();
411
412 foreach ($tickets as $ticket) {
413 $ticket->deleteTicket();
414 }
415
416 return [
417 'message' => __(count($tickets) . ' tickets have been deleted', 'fluent-support')
418 ];
419 } else if ($action == 'assign_agent') {
420 $agentId = absint($request->get('agent_id'));
421 if (!$agentId) {
422 $this->sendError([
423 'message' => __('agent_id param is required', 'fluent-support')
424 ]);
425 }
426
427 $agent = Agent::findOrFail($agentId);
428
429 $query->where(function ($q) use ($agent) {
430 $q->where('agent_id', '!=', $agent->id)
431 ->orWhereNull('agent_id');
432 });
433
434 $tickets = $query->get();
435
436 foreach ($tickets as $ticket) {
437 $ticket->agent_id = $agent->id;
438 $ticket->save();
439 do_action('fluent_support/agent_assigned_to_ticket', $agent, $ticket);
440 }
441
442 return [
443 'message' => __(count($tickets) . ' tickets has been assigned to', 'fluent-support') . ' ' . $agent->full_name
444 ];
445 } else if ($action == 'assign_tags') {
446
447 $tags = array_filter(array_map('absint', $request->get('tag_ids', [])));
448 if (!$tags) {
449 $this->sendError([
450 'message' => __('tag_ids param is required', 'fluent-support')
451 ]);
452 }
453
454 $tickets = $query->get();
455
456 foreach ($tickets as $ticket) {
457 $ticket->applyTags($tags);
458 }
459
460 return [
461 'message' => __('Selected tags has been added to tickets', 'fluent-support')
462 ];
463
464 }
465
466 $this->sendError([
467 'message' => __('Sorry no action found as available', 'fluent-support')
468 ]);
469 }
470
471 public function doBulkReplies(Request $request)
472 {
473 $data = $request->all();
474 $this->validate($data, [
475 'content' => 'required',
476 'ticket_ids' => 'required|array'
477 ]);
478
479 $ticketIds = $request->get('ticket_ids');
480 $ticketIds = array_filter($ticketIds, 'absint');
481
482 $agent = Helper::getAgentByUserId();
483
484 $hasAllPermission = PermissionManager::currentUserCan('fst_manage_other_tickets');
485
486 $query = Ticket::whereIn('id', $ticketIds)->where('status', '!=', 'closed');
487
488 if (!$hasAllPermission) {
489 $query->where('agent_id', $agent->id);
490 }
491
492 $tickets = $query->get();
493
494 if ($tickets->isEmpty()) {
495 $this->sendError([
496 'message' => __('Sorry no tickets found based on your filter and bulk actions', 'fluent-support')
497 ]);
498 }
499
500 $responseData = [
501 'content' => $request->get('content'),
502 'conversation_type' => $request->get('conversation_type', 'response'),
503 'close_ticket' => $request->get('close_ticket', 'no')
504 ];
505
506 $attachments = $request->get('attachments', []);
507
508 if ($attachments) {
509 $attachments = Attachment::whereNull('ticket_id')
510 ->orderBy('id', 'asc')
511 ->whereIn('file_hash', $attachments)
512 ->get();
513 }
514
515
516 $responseService = new ResponseService();
517
518 foreach ($tickets as $ticket) {
519 if ($attachments) {
520 $responseData['attachments'] = [];
521 foreach ($attachments as $attachment) {
522 $attachedFile = $attachment->replicate();
523 $attachedFile->ticket_id = $ticket->id;
524 $attachedFile->save();
525 $responseData['attachments'][] = $attachedFile->file_hash;
526 }
527 }
528
529 $responseService->createResponse($responseData, $agent, $ticket);
530 }
531
532
533 return [
534 'message' => __('Response has been added to the selected tickets', 'fluent-support')
535 ];
536
537 }
538
539 public function deleteResponse(Request $request, $ticketId, $responseId)
540 {
541 $ticket = Ticket::findOrFail($ticketId);
542 $response = Conversation::findOrFail($responseId);
543 $agent = Helper::getAgentByUserId();
544
545 $hasAllPermission = PermissionManager::currentUserCan('fst_manage_other_tickets');
546
547 if (!$hasAllPermission) {
548 if ($ticket->agent_id != $agent->id) {
549 return $this->sendError([
550 'message' => __('Sorry, You do not have permission to delete this response', 'fluent-support')
551 ]);
552 }
553 }
554
555 Conversation::where('id', $response->id)->delete();
556
557 return [
558 'message' => __('Selected response has been deleted', 'fluent-support')
559 ];
560
561 }
562
563 public function updateResponse(Request $request, $ticketId, $responseId)
564 {
565 $data = $request->all();
566
567 $this->validate($data, [
568 'content' => 'required'
569 ]);
570
571 $ticket = Ticket::findOrFail($ticketId);
572 $response = Conversation::findOrFail($responseId);
573 $agent = Helper::getAgentByUserId();
574
575 $hasAllPermission = PermissionManager::currentUserCan('fst_manage_other_tickets');
576
577 if (!$hasAllPermission) {
578 if ($ticket->agent_id != $agent->id) {
579 return $this->sendError([
580 'message' => __('Sorry, You do not have permission to delete this response', 'fluent-support')
581 ]);
582 }
583 }
584
585 $response->content = wp_unslash(wp_kses_post($data['content']));
586 $response->save();
587
588 return [
589 'message' => __('Selected response has been updated', 'fluent-support'),
590 'response' => $response
591 ];
592 }
593
594 public function getLiveActivity(Request $request, $ticketId)
595 {
596 $agent = Helper::getAgentByUserId();
597
598 return [
599 'live_activity' => TicketHelper::getActivity($ticketId, $agent->id)
600 ];
601 }
602
603 public function removeLiveActivity(Request $request, $ticketId)
604 {
605 $agent = Helper::getAgentByUserId();
606
607 return [
608 'result' => TicketHelper::removeFromActivities($ticketId, $agent->id),
609 'agent_id' => $agent->id
610 ];
611 }
612
613 public function addTag(Request $request, $ticketId)
614 {
615 $ticket = Ticket::findOrFail($ticketId);
616
617 $tagId = intval($request->get('tag_id'));
618
619 if (!$ticket->hasTag($tagId)) {
620 $ticket->tags()->attach($tagId, ['source_type' => 'ticket_tag']);
621 }
622
623 return [
624 'message' => __('Tag has been added to this ticket', 'fluent-support'),
625 'tags' => $ticket->tags
626 ];
627 }
628
629 public function detachTag($ticketId, $tagId)
630 {
631 $ticket = Ticket::findOrFail($ticketId);
632 $ticket->tags()->detach($tagId);
633
634 return [
635 'message' => __('Tag has been removed from this ticket', 'fluent-support'),
636 'tags' => $ticket->tags
637 ];
638 }
639
640 public function changeTicketCustomer(Request $request)
641 {
642 $updateCustomer = Ticket::where('id', $request->get('ticket_id'))
643 ->update(['customer_id' => $request->get('customer')]);
644 return [
645 'message' => __('Customer has been updated', 'fluent-support'),
646 'updatedCustomer' => $updateCustomer
647 ];
648 }
649
650 public function getTicketCustomData(Request $request, $ticketId)
651 {
652 if (!defined('FLUENTSUPPORTPRO')) {
653 return [
654 'custom_data' => [],
655 'rendered_fields' => []
656 ];
657 }
658
659 $ticket = Ticket::findOrFail($ticketId);
660
661 return [
662 'custom_data' => (object)$ticket->customData(),
663 'rendered_fields' => \FluentSupportPro\App\Services\CustomFieldsService::getRenderedPublicFields($ticket->customer)
664 ];
665 }
666
667 public function syncFluentCrmTags(Request $request)
668 {
669
670 if (!defined('FLUENTCRM')) {
671 return $this->sendError([
672 'message' => __('FluentCRM is not installed', 'fluent-support')
673 ]);
674 }
675
676 $contactId = absint($request->get('contact_id'));
677
678 if (!$contactId) {
679 return $this->sendError([
680 'message' => __('Contact could not be found', 'fluent-support')
681 ]);
682 }
683
684 $tagIds = array_filter($request->get('tags', []), 'absint');
685 $canAddTags = \FluentCrm\App\Services\PermissionManager::currentUserCan('fcrm_manage_contacts');
686 $canAddTags = apply_filters('fluent_support/can_user_add_tags_to_customer', $canAddTags);
687
688 if (!$canAddTags) {
689 return $this->sendError([
690 'message' => __('Sorry you do not have permission to add contact tags', 'fluent-support')
691 ]);
692 }
693
694 $contact = \FluentCrm\App\Models\Subscriber::findOrFail($contactId);
695
696 $existingTags = $contact->tags;
697 $existingTagIds = [];
698 foreach ($existingTags as $tag) {
699 $existingTagIds[] = $tag->id;
700 }
701 $newTagIds = array_diff($tagIds, $existingTagIds);
702 $removedTagIds = array_diff($existingTagIds, $tagIds);
703
704 if ($newTagIds) {
705 $contact->attachTags($newTagIds);
706 }
707
708 if ($removedTagIds) {
709 $contact->detachTags($removedTagIds);
710 }
711
712
713 return [
714 'tags' => $contact->tags,
715 'message' => __('FluentCRM contact tags has been updated', 'fluent-support')
716 ];
717
718 }
719 }
720