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

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