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

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