PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 1.4.2
Fluent Support – Helpdesk & Customer Support Ticket System v1.4.2
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.4.2, at app/Http/Controllers/TicketController.php

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