PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 1.10.3
Fluent Support – Helpdesk & Customer Support Ticket System v1.10.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 / Services / Helper.php

Helper.php in Fluent Support – Helpdesk & Customer Support Ticket System 1.10.3, at app/Services/Helper.php

1,458 lines 53.7 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\Services;
4
5 use FluentSupport\App\App;
6 use FluentSupport\App\Models\Agent;
7 use FluentSupport\App\Models\Ticket;
8 use FluentSupport\App\Models\Conversation;
9 use FluentSupport\App\Models\Customer;
10 use FluentSupport\App\Models\MailBox;
11 use FluentSupport\App\Models\Meta;
12 use FluentSupport\App\Models\AIActivityLogs;
13 use FluentSupport\App\Models\Person;
14 use FluentSupport\App\Models\Product;
15 use FluentSupport\App\Services\Includes\UploadService;
16 use FluentSupport\App\Services\EmailNotification\Settings;
17 use FluentSupport\Framework\Support\Arr;
18
19 /**
20 * Helper - REST API Helper Class
21 *
22 * App helper for REST API
23 *
24 * @package FluentSupport\App\Services
25 *
26 * @version 1.0.0
27 */
28 class Helper
29 {
30 public static function FluentSupport($module = null)
31 {
32 return App::getInstance($module);
33 }
34
35 /**
36 * Get agent information by user id
37 * The function will get user id as parameter or get id from session and return agent information
38 * @param null $userId
39 * @return false | Agent
40 */
41 public static function getAgentByUserId($userId = null)
42 {
43 if ($userId === null) {
44 $userId = get_current_user_id();
45 }
46 if (!$userId) {
47 return false;
48 }
49 return Agent::where('user_id', $userId)->first();
50 }
51
52 /**
53 * This function will return the list of ticket priorities list for customer
54 *
55 * @return mixed
56 */
57 public static function customerTicketPriorities()
58 {
59 return apply_filters('fluent_support/customer_ticket_priorities', [
60 'normal' => __('Normal', 'fluent-support'),
61 'medium' => __('Medium', 'fluent-support'),
62 'critical' => __('Critical', 'fluent-support')
63 ]);
64 }
65
66 /**
67 * This function will return the list of ticket priorities list for Admin
68 *
69 * @return mixed
70 */
71 public static function adminTicketPriorities()
72 {
73 return apply_filters('fluent_support/admin_ticket_priorities', [
74 'normal' => __('Normal', 'fluent-support'),
75 'medium' => __('Medium', 'fluent-support'),
76 'critical' => __('Critical', 'fluent-support')
77 ]);
78 }
79
80
81 /**
82 * This function will return ticket status group
83 *
84 * @return mixed
85 */
86 public static function ticketStatusGroups()
87 {
88 return apply_filters('fluent_support/ticket_status_groups', [
89 'open' => ['new', 'active'],
90 'active' => ['active'],
91 'closed' => ['closed'],
92 'new' => ['new'],
93 'all' => []
94 ]);
95 }
96
97 /**
98 * This function will return custom ticket status group
99 *
100 * @return mixed
101 */
102 public static function changeableTicketStatuses()
103 {
104 $ticketStatus = static::ticketStatusGroups();
105
106 unset($ticketStatus['all']);
107 unset($ticketStatus['open']);
108
109 return apply_filters('fluent_support/changeable_ticket_statuses', $ticketStatus);
110 }
111
112 /**
113 * This function will return ticket status list
114 *
115 * @return mixed
116 */
117 public static function ticketStatuses()
118 {
119 return apply_filters('fluent_support/ticket_statuses', [
120 'new' => __('New', 'fluent-support'),
121 'active' => __('Active', 'fluent-support'),
122 'closed' => __('Closed', 'fluent-support'),
123 ]);
124 }
125
126 public static function getTkStatusesByGroupName($groupName)
127 {
128 $groups = self::ticketStatusGroups();
129 return Arr::get($groups, $groupName, []);
130 }
131
132 public static function ticketAcceptedFileMiles()
133 {
134 $groups = self::getMimeGroups();
135 $globalSettings = (new Settings())->globalBusinessSettings();
136
137 if (empty($globalSettings['accepted_file_types'])) {
138 return apply_filters('fluent_support/accepted_ticket_mimes', []);
139 }
140
141 $mimes = [];
142 $typesGroups = Arr::only($groups, $globalSettings['accepted_file_types']);
143 foreach ($typesGroups as $mimesGroup) {
144 $mimes = array_merge($mimes, $mimesGroup['mimes']);
145 }
146
147 return apply_filters('fluent_support/accepted_ticket_mimes', $mimes);
148 }
149
150 public static function getAcceptedMimeHeadings()
151 {
152 $groups = self::getMimeGroups();
153 $globalSettings = (new Settings())->globalBusinessSettings();
154
155 if (empty($globalSettings['accepted_file_types'])) {
156 return [];
157 }
158
159 $mimeNames = [];
160 $typesGroups = Arr::only($groups, $globalSettings['accepted_file_types']);
161 foreach ($typesGroups as $mimesGroup) {
162 $mimeNames[] = $mimesGroup['title'];
163 }
164
165 return $mimeNames;
166 }
167
168 public static function getFileUploadMessage()
169 {
170 $mimeHeadings = self::getAcceptedMimeHeadings();
171 $settings = (new Settings())->globalBusinessSettings();
172 $maxFileSize = floatval($settings['max_file_size']);
173
174 // translators: %1$s is a comma-separated list of supported file types, %2$.01f is the maximum file size in megabytes
175 return sprintf(__('Supported Types: %1$s and max file size: %2$.01fMB', 'fluent-support'), implode(', ', $mimeHeadings), $maxFileSize);
176 }
177
178 public static function getMimeGroups()
179 {
180 return apply_filters('fluent_support/mime_groups', [
181 'images' => [
182 'title' => __('Photos', 'fluent-support'),
183 'mimes' => [
184 'image/gif',
185 'image/ief',
186 'image/jpeg',
187 'image/webp',
188 'image/pjpeg',
189 'image/ktx',
190 'image/png'
191 ]
192 ],
193 'csv' => [
194 'title' => __('CSV', 'fluent-support'),
195 'mimes' => [
196 'application/csv',
197 'application/txt',
198 'text/csv',
199 'text/plain',
200 'text/comma-separated-values',
201 'text/anytext',
202 ]
203 ],
204 'documents' => [
205 'title' => __('PDF/Docs', 'fluent-support'),
206 'mimes' => [
207 'application/excel',
208 'application/vnd.ms-excel',
209 'application/vnd.msexcel',
210 'application/octet-stream',
211 'application/pdf',
212 'application/msword',
213 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
214 ]
215 ],
216 'zip' => [
217 'title' => __('Zip', 'fluent-support'),
218 'mimes' => [
219 'application/zip'
220 ]
221 ],
222 'json' => [
223 'title' => __('JSON', 'fluent-support'),
224 'mimes' => [
225 'application/json',
226 'application/jsonml+json'
227 ]
228 ]
229 ]);
230 }
231
232 /**
233 * getOption method will return settings using key
234 * This method will get key as parameter, fetch data from database, beautify the data and return
235 * @param $key
236 * @param string $default
237 * @return mixed|string
238 */
239 public static function getOption($key, $default = '')
240 {
241 //Get settings from meta table using the key
242 $data = Meta::where('object_type', 'option')
243 ->where('key', $key)
244 ->first();
245
246 if ($data) {
247 $value = static::safeUnserialize($data->value);
248 if ($value) {
249 return $value;
250 }
251 }
252
253 return $default;
254 }
255
256 /**
257 * updateOption method will update or insert settings
258 * This method will get key and value as parameter, check exists or not. If exist update value by key, else insert value for the key
259 * @param $key
260 * @param $value
261 * @return mixed
262 */
263 public static function updateOption($key, $value)
264 {
265 //Get settings from meta table using the key
266 $data = Meta::where('object_type', 'option')
267 ->where('key', $key)
268 ->first();
269
270 //If data is available, update existing data and return
271 if ($data) {
272 return Meta::where('id', $data->id)
273 ->update([
274 'value' => maybe_serialize($value)
275 ]);
276 }
277
278 //If newly submit, create new record and return
279 return Meta::insert([
280 'object_type' => 'option',
281 'key' => $key,
282 'value' => maybe_serialize($value)
283 ]);
284 }
285
286 public static function deleteOption($key)
287 {
288 return Meta::where('object_type', 'option')
289 ->where('key', $key)
290 ->delete();
291 }
292
293 /**
294 * getIntegrationOption method will return the integration settings by integration key
295 * @param $key
296 * @param string $default
297 * @return mixed|string
298 */
299 public static function getIntegrationOption($key, $default = '')
300 {
301 $data = Meta::where('object_type', 'integration_settings')
302 ->where('key', $key)
303 ->first();
304
305 if ($data) {
306 $value = static::safeUnserialize($data->value);
307 if ($value) {
308 return $value;
309 }
310 }
311
312 return $default;
313 }
314
315 /**
316 * updateIntegrationOption method will update existing settings or create new settings by integration key
317 * @param $key
318 * @param $value
319 * @return mixed
320 */
321 public static function updateIntegrationOption($key, $value)
322 {
323 $data = Meta::where('object_type', 'integration_settings')
324 ->where('key', $key)
325 ->first();
326
327 if ($data) {
328 return Meta::where('id', $data->id)
329 ->update([
330 'value' => maybe_serialize($value)
331 ]);
332 }
333
334 return Meta::insert([
335 'object_type' => 'integration_settings',
336 'key' => $key,
337 'value' => maybe_serialize($value)
338 ]);
339 }
340
341 public static function getTicketViewUrl($ticket)
342 {
343 $baseUrl = self::getPortalBaseUrl();
344 return $baseUrl . '/#/ticket/' . $ticket->id . '/view';
345 }
346
347 public static function getTicketViewSignedUrl($ticket)
348 {
349 if (!self::isPublicSignedTicketEnabled()) {
350 return self::getTicketViewUrl($ticket);
351 }
352
353 $baseUrl = self::getPortalBaseUrl();
354
355 $baseUrl = add_query_arg([
356 'fs_view' => 'ticket',
357 'support_hash' => $ticket->hash,
358 'ticket_id' => $ticket->id,
359 ], $baseUrl);
360
361 return $baseUrl . '#/ticket/' . $ticket->id . '/view';
362 }
363
364 public static function saveOpenAIData($objectType, $key, $data)
365 {
366 $serializedData = maybe_serialize($data);
367
368 $previousValue = Meta::where('object_type', $objectType)->first();
369
370 if ($previousValue) {
371 return Meta::where('object_type', $objectType)->update([
372 'value' => $serializedData
373 ]);
374 } else {
375 return Meta::insert([
376 'object_type' => $objectType,
377 'key' => $key,
378 'value' => $serializedData
379 ]);
380 }
381
382 }
383
384 public static function authorizeChatGPTAPIKey($data)
385 {
386 return wp_remote_get('https://api.openai.com/v1/models', [
387 'headers' => [
388 'Authorization' => 'Bearer ' . $data['api_key'],
389 'Content-Type' => 'application/json'
390 ]
391 ]);
392 }
393
394 public static function isPublicSignedTicketEnabled()
395 {
396 $businessSettings = self::getBusinessSettings();
397
398 return (Arr::get($businessSettings, 'disable_public_ticket') != 'yes');
399 }
400
401 public static function getTicketAdminUrl($ticket)
402 {
403 $baseUrl = self::getPortalAdminBaseUrl();
404 return $baseUrl . 'tickets/' . $ticket->id . '/view';
405 }
406
407 /**
408 * getPortalBaseUrl will get the portal page id and return link of the page
409 * @return mixed
410 */
411 public static function getPortalBaseUrl()
412 {
413 $businessSettings = self::getBusinessSettings();
414 $baseUrl = get_permalink($businessSettings['portal_page_id']);
415 $baseUrl = rtrim($baseUrl, '/\\');
416 return apply_filters('fluent_support/portal_base_url', $baseUrl);
417 }
418
419 public static function getPortalAdminBaseUrl()
420 {
421 return apply_filters('fluent_support/portal_admin_base_url', admin_url('admin.php?page=fluent-support/#/'));
422 }
423
424 public static function getBusinessSettings($key = null)
425 {
426 static $settings;
427
428 if ($settings && $key) {
429 return Arr::get($settings, $key);
430 }
431
432 if ($settings) {
433 return $settings;
434 }
435
436 $settings = (new Settings())->globalBusinessSettings();
437
438 if ($key) {
439 return Arr::get($settings, $key);
440 }
441 return $settings;
442 }
443
444 public static function isAgentFeedbackEnabled()
445 {
446 return self::getBusinessSettings('agent_feedback_rating', 'no') == 'yes';
447 }
448
449 public static function getTicketMeta($ticketId, $key, $default = '')
450 {
451 $data = Meta::where('object_type', 'ticket_meta')
452 ->where('key', $key)
453 ->where('object_id', $ticketId)
454 ->first();
455
456 if ($data) {
457 $value = static::safeUnserialize($data->value);
458 if ($value) {
459 return $value;
460 }
461 }
462
463 return $default;
464 }
465
466 public static function updateTicketMeta($ticketId, $key, $value)
467 {
468 $data = Meta::where('object_type', 'ticket_meta')
469 ->where('key', $key)
470 ->where('object_id', $ticketId)
471 ->first();
472
473 if ($data) {
474 return Meta::where('id', $data->id)
475 ->update([
476 'value' => maybe_serialize($value)
477 ]);
478 }
479
480 return Meta::insert([
481 'object_type' => 'ticket_meta',
482 'object_id' => $ticketId,
483 'key' => $key,
484 'value' => maybe_serialize($value)
485 ]);
486 }
487
488 public static function getWPPages()
489 {
490 $pages = (self::FluentSupport())->app->db
491 ->table('posts')
492 ->select(['ID', 'post_title'])
493 ->where('post_type', 'page')
494 ->where('post_status', 'publish')
495 ->latest('ID')
496 ->get();
497 $formattedPages = [];
498 foreach ($pages as $page) {
499 $formattedPages[] = [
500 'id' => intval($page->ID),
501 'title' => $page->post_title ?: __('(no title)', 'fluent-support')
502 ];
503 }
504 return $formattedPages;
505 }
506
507 public static function getDefaultMailBox()
508 {
509 $mailbox = MailBox::where('is_default', 'yes')->first();
510
511 if ($mailbox) {
512 return $mailbox;
513 }
514
515 return MailBox::oldest('id')->first();
516 }
517
518 public static function getCurrentAgent()
519 {
520 // If user is logged in then return the agent by user id.
521 // This `get_current_user_id` function is WP function and
522 // it returns user id if user is logged in.
523 if (get_current_user_id()) {
524 return Agent::where('user_id', get_current_user_id())->first();
525 }
526 }
527
528 public static function getCurrentCustomer()
529 {
530 // If user is logged in then return the customer by user id.
531 // This `get_current_user_id` function is WP function and
532 // it returns user id if user is logged in.
533 if (get_current_user_id()) { //if user is logged in
534 return Customer::where('user_id', get_current_user_id())->first();
535 }
536 }
537
538 public static function getCurrentPerson()
539 {
540 // If user is logged in then return the person(agent/customer) by user id.
541 // This `get_current_user_id` function is WP function and
542 // it returns user id if user is logged in.
543 if (get_current_user_id()) {
544 return Person::where('user_id', get_current_user_id())
545 ->orderBy('id', 'ASC')
546 ->first();
547 }
548 return null;
549 }
550
551 public static function getCustomerByID($customerid)
552 {
553 return Customer::where('id', $customerid)->first();
554 }
555
556 public static function sanitizeOrderValue($orderType = '')
557 {
558 $orderBys = ['ASC', 'DESC'];
559
560 $orderType = trim(strtoupper($orderType));
561
562 return in_array($orderType, $orderBys) ? $orderType : 'DESC';
563 }
564
565 public static function getFluentCRMTagConfig()
566 {
567 if (!defined('FLUENTCRM')) {
568 return [
569 'can_add_tags' => false,
570 'tags' => [],
571 'lists' => [],
572 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/fluentcrm-logo.svg',
573 'icon' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/fluent-crm-icon.png',
574 ];
575 }
576
577 $canAddTags = \FluentCrm\App\Services\PermissionManager::currentUserCan('fcrm_manage_contacts');
578
579 $canAddTags = apply_filters('fluent_support/can_user_add_tags_to_customer', $canAddTags);
580 $crmTags = [];
581 $crmLists = [];
582 if ($canAddTags) {
583 $crmTags = \FluentCrm\App\Models\Tag::select(['id', 'title'])->oldest('title')->get();
584 $crmLists = \FluentCrm\App\Models\Lists::select(['id', 'title'])->oldest('title')->get();
585 }
586
587 $crmConfigs = [
588 'can_add_tags' => $canAddTags,
589 'tags' => $crmTags,
590 'lists' => $crmLists,
591 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/fluentcrm-logo.svg',
592 'icon' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/fluent-crm-icon.png',
593 ];
594
595 if (defined('FLUENTCRM')) {
596 $crmConfigs['contacts'] = []; //(new \FluentCrm\App\Models\Subscriber)->get();
597 }
598
599 return $crmConfigs;
600 }
601
602 /**
603 * getFluentCrmContactData method will get information from fluent crm using user email
604 * @param $customer
605 * @return array|false
606 */
607 public static function getFluentCrmContactData($customer)
608 {
609 if (!defined('FLUENTCRM')) {
610 return false;
611 }
612 //Get contact info from FluentCRM using customer email
613 $contact = \FluentCrmApi('contacts')->getContactByUserRef($customer->email);
614 if ($contact) {
615 $tags = $contact->tags;
616 $lists = $contact->lists;
617 $urlBase = apply_filters('fluentcrm_menu_url_base', admin_url('admin.php?page=fluentcrm-admin#/'));
618 $crmProfileUrl = $urlBase . 'subscribers/' . $contact->id;
619
620 //Return contact data
621 return [
622 'id' => $contact->id,
623 'first_name' => $contact->first_name,
624 'last_name' => $contact->last_name,
625 'full_name' => $contact->full_name,
626 'name_mismatch' => $contact->full_name != $customer->full_name,
627 'tags' => $tags,
628 'lists' => $lists,
629 'status' => $contact->status,
630 'stats' => $contact->stats(),
631 'view_url' => $crmProfileUrl
632 ];
633 }
634
635 return false;
636 }
637
638 public static function openAIIntegrationStatus() {
639 $chatGPTSettingsData = Meta::where('object_type', '_fs_openai_settings')->value('value');
640
641 if ($chatGPTSettingsData) {
642 $settings = static::safeUnserialize($chatGPTSettingsData);
643 return !empty($settings['api_key']);
644 }
645
646 return false;
647 }
648
649 public static function fluentBotIntegrationStatus()
650 {
651 $settings = static::safeUnserialize(
652 Meta::where('object_type', 'fluent_bot_settings')->value('value')
653 );
654
655 return !empty($settings['isEnabled']) && $settings['isEnabled'] === 'true';
656 }
657
658
659 public static function showTicketSummaryAdminBar()
660 {
661 $data = self::getOption('global_business_settings');
662
663 if ($data && isset($data["enable_admin_bar_summary"]) && $data["enable_admin_bar_summary"] == 'yes') {
664 return true;
665 }
666
667 return false;
668 }
669
670 public static function generateMessageID($email)
671 {
672 $emailParts = explode('@', $email);
673 if (count($emailParts) != 2) {
674 return false;
675 }
676
677 $emailDomain = $emailParts[1];
678 try {
679 return sprintf(
680 "<%s.%s@%s>",
681 base_convert((int)microtime(true), 10, 36),
682 base_convert(bin2hex(openssl_random_pseudo_bytes(8)), 16, 36),
683 $emailDomain
684 );
685 } catch (\Exception $exception) {
686 return false;
687 }
688 }
689
690 public static function getExportOptions()
691 {
692 $data = [
693 'Agent First Name' => __('Agent First Name', 'fluent-support'),
694 'Agent Last Name' => __('Agent Last Name', 'fluent-support'),
695 'Agent Full Name' => __('Agent Full Name', 'fluent-support'),
696 'Responses' => __('Responses', 'fluent-support'),
697 'Interactions' => __('Interactions', 'fluent-support'),
698 'Open Tickets' => __('Open Tickets', 'fluent-support'),
699 'Closed' => __('Closed', 'fluent-support'),
700 'Waiting Tickets' => __('Waiting Tickets', 'fluent-support'),
701 'Average Waiting' => __('Average Waiting', 'fluent-support'),
702 'Max Waiting' => __('Max Waiting', 'fluent-support'),
703 ];
704
705 if (Helper::isAgentFeedbackEnabled()) {
706 $data['Likes'] = __('Likes', 'fluent-support');
707 $data['Dislikes'] = __('Dislikes', 'fluent-support');
708 }
709
710 return $data;
711 }
712
713 public static function getAuthProvider()
714 {
715 if (defined('FLUENT_AUTH_PLUGIN_PATH')) {
716 $settings = \FluentAuth\App\Helpers\Helper::getAuthFormsSettings();
717 if ($settings['enabled'] == 'yes') {
718 return 'fluent_auth';
719 }
720 }
721
722 return 'fluent_support';
723 }
724
725 public static function getDriversKey(){
726 return [
727 'dropbox_settings',
728 'google_drive_settings',
729 'local'
730 ];
731 }
732
733
734 public static function getUploadDriverKey()
735 {
736 if (!defined('FLUENTSUPPORTPRO')) {
737 return 'local';
738 }
739
740 $driver = self::getOption('file_upload_driver');
741
742 if ($driver) {
743 return $driver;
744 }
745
746 // Now guess the driver and save it
747
748 // check if dropbox is enabled
749 $dropboxSettings = self::getIntegrationOption('dropbox_settings', null);
750 if ($dropboxSettings) {
751 $dropBoxEnabled = Meta::where('object_type', 'enabled_upload_drivers')
752 ->where('key', 'dropbox_settings')
753 ->where('value', 'yes')
754 ->first();
755
756 if ($dropBoxEnabled) {
757 $driver = 'dropbox';
758 self::updateOption('file_upload_driver', $driver);
759 return $driver;
760 }
761 }
762
763 // check if google drive is enabled
764 $googleDriveSettings = self::getIntegrationOption('google_drive_settings', null);
765
766 if ($googleDriveSettings) {
767 $googleDriveEnabled = Meta::where('object_type', 'enabled_upload_drivers')
768 ->where('key', 'google_drive_settings')
769 ->where('value', 'yes')
770 ->first();
771
772 if ($googleDriveEnabled) {
773 $driver = 'google_drive';
774 self::updateOption('file_upload_driver', $driver);
775 return $driver;
776 }
777 }
778
779 self::updateOption('file_upload_driver', 'local');
780 return 'local';
781 }
782
783 public static function getIntegrationStatuses()
784 {
785 $connections = [
786 'woocommerce' => [
787 'title' => __('WooCommerce', 'fluent-support'),
788 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/woocommerce.png',
789 'is_integrated' => defined('WC_PLUGIN_FILE'),
790 'description' => __('The most popular e-commerce platform for WordPress', 'fluent-support'),
791 'doc_url' => 'https://fluentsupport.com/docs/woocommerce-integration/',
792 ],
793 'fluent-cart' => [
794 'title' => __('Fluent Cart', 'fluent-support'),
795 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/fluent-cart.webp',
796 'is_integrated' => defined('FLUENTCART_VERSION'),
797 'description' => __('A New Era of eCommerce with WordPress', 'fluent-support'),
798 'doc_url' => 'https://fluentsupport.com/docs/fluentcart-integration/',
799 ],
800 'lifter-lms' => [
801 'title' => __('LifterLMS', 'fluent-support'),
802 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/lifter-lms.png',
803 'is_integrated' => defined('LLMS_PLUGIN_FILE'),
804 'description' => __('Course and e-learning platform built for WordPress', 'fluent-support'),
805 'doc_url' => 'https://fluentsupport.com/docs/lifterlms-integration/',
806 ],
807 'slack' => [
808 'title' => __('Slack', 'fluent-support'),
809 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/slack.png',
810 'is_integrated' => self::getFSIntegrationStatus('slack_settings'),
811 'description' => __('Business communication platform designed to scale', 'fluent-support'),
812 'doc_url' => 'https://fluentsupport.com/docs/managing-tickets-using-slack/',
813 ],
814 'pm-pro' => [
815 'title' => __('Paid Memberships Pro', 'fluent-support'),
816 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/pmpro.png',
817 'is_integrated' => defined('PMPRO_VERSION'),
818 'description' => __('The ultimate platform for any member-focused business', 'fluent-support'),
819 'doc_url' => 'https://fluentsupport.com/docs/paid-membership-pro-integration/',
820 ],
821 'tutor-lms' => [
822 'title' => __('Tutor LMS', 'fluent-support'),
823 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/tutor-lms.png',
824 'is_integrated' => defined('TUTOR_VERSION'),
825 'description' => __('Course and e-learning platform built for WordPress', 'fluent-support'),
826 'doc_url' => 'https://fluentsupport.com/docs/tutorlms-integration/',
827 ],
828 'telegram' => [
829 'title' => __('Telegram', 'fluent-support'),
830 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/telegram.jpeg',
831 'is_integrated' => self::getFSIntegrationStatus('telegram_settings'),
832 'description' => __('Business communication platform designed for security', 'fluent-support'),
833 'doc_url' => 'https://fluentsupport.com/docs/managing-tickets-using-telegram/',
834 ],
835 'fluent-crm' => [
836 'title' => __('FluentCRM', 'fluent-support'),
837 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/fluent-crm.png',
838 'is_integrated' => defined('FLUENTCRM'),
839 'description' => __('Self-hosted email and marketing automation for WordPress', 'fluent-support'),
840 'doc_url' => 'https://fluentsupport.com/docs/fluentcrm-integration/',
841 ],
842 'fluent-community' => [
843 'title' => __('Fluent Community', 'fluent-support'),
844 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/fluent-community.png',
845 'is_integrated' => defined('FLUENT_COMMUNITY_PLUGIN_VERSION'),
846 'description' => __('Build and manage vibrant online communities with integrated LMS features directly within WordPress.', 'fluent-support'),
847 'doc_url' => 'https://fluentsupport.com/docs/fluentcommunity-integration/',
848 ],
849 'fluent-forms' => [
850 'title' => __('Fluent FORMS', 'fluent-support'),
851 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/fluent-forms.png',
852 'is_integrated' => defined('FLUENTFORM'),
853 'description' => __('A robust form plugin suitable for any business', 'fluent-support'),
854 'doc_url' => 'https://fluentsupport.com/docs/fluent-form-integration/',
855 ],
856 'buddy-boss' => [
857 'title' => __('BuddyBoss', 'fluent-support'),
858 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/buddy-boss.png',
859 'is_integrated' => defined('BP_PLUGIN_DIR'),
860 'description' => __('Powerful platform for any member-focused business', 'fluent-support'),
861 'doc_url' => 'https://fluentsupport.com/docs/buddyboss-integration/'
862 ],
863 'discord' => [
864 'title' => __('Discord', 'fluent-support'),
865 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/discord.png',
866 'is_integrated' => self::getFSIntegrationStatus('discord_settings'),
867 'description' => __('Business communication platform designed for tech', 'fluent-support'),
868 'doc_url' => 'https://fluentsupport.com/docs/managing-tickets-using-discord/',
869 ],
870 'wishlist-member' => [
871 'title' => __('WishList Member', 'fluent-support'),
872 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/wishlist-member.png',
873 'is_integrated' => defined('WLM3_PLUGIN_VERSION'),
874 'description' => __('Powerful platform for any member-focused business', 'fluent-support'),
875 'doc_url' => 'https://fluentsupport.com/docs/wishlist-member-integration/',
876 ],
877 'easy-digital-downloads' => [
878 'title' => __('Easy Digital Downloads', 'fluent-support'),
879 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/easy-digital-downloads.png',
880 'is_integrated' => class_exists('\Easy_Digital_Downloads'),
881 'description' => __('The ultimate WordPress platform for digital products', 'fluent-support'),
882 'doc_url' => 'https://fluentsupport.com/docs/edd-integration/',
883 ],
884 'restrict-content-pro' => [
885 'title' => __('Restrict Content pro', 'fluent-support'),
886 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/restrict-content-pro.png',
887 'is_integrated' => class_exists('\Restrict_Content_Pro' ),
888 'description' => __('Powerful platform for any member-focused business', 'fluent-support'),
889 'doc_url' => 'https://fluentsupport.com/docs/restrict-content-pro-integration/',
890 ],
891 'better-docs' => [
892 'title' => __('BetterDocs', 'fluent-support'),
893 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/better-docs.png',
894 'is_integrated' => false,
895 'description' => __('The standard plugin for knowledge base and documentation', 'fluent-support'),
896 'doc_url' => 'https://fluentsupport.com/docs/betterdocs-integration/',
897 ],
898 'whatsapp' => [
899 'title' => __('WhatsApp', 'fluent-support'),
900 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/whatsapp.jpeg',
901 'is_integrated' => self::getFSIntegrationStatus('twilio_settings'),
902 'description' => __('Business communication platform designed for privacy', 'fluent-support'),
903 'doc_url' => 'https://fluentsupport.com/docs/whatsapp-integration-via-twilio/',
904 ],
905 'paymattic' => [
906 'title' => __('Paymattic', 'fluent-support'),
907 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/paymattic.png',
908 'is_integrated' => defined('WPPAYFORM_VERSION'),
909 'description' => __('All-in-one payment gateway designed for WordPress', 'fluent-support'),
910 'doc_url' => 'https://paymattic.com/docs/how-to-integrate-fluent-support-with-paymattic-in-wordpress/',
911 ],
912 'learn-dash' => [
913 'title' => __('LearnDash', 'fluent-support'),
914 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/learn-dash.png',
915 'is_integrated' => defined('LEARNDASH_VERSION'),
916 'description' => __('The leading course platform built for WordPress', 'fluent-support'),
917 'doc_url' => 'https://fluentsupport.com/docs/learndash-integration/',
918 ],
919 'learn-press' => [
920 'title' => __('LearnPress', 'fluent-support'),
921 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/learn-press.png',
922 'is_integrated' => defined('LP_PLUGIN_FILE'),
923 'description' => __('Course and e-learning platform built for WordPress', 'fluent-support'),
924 'doc_url' => 'https://fluentsupport.com/docs/learnpress-integration/',
925 ],
926 'google-drive' => [
927 'title' => __('Google Drive', 'fluent-support'),
928 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/google-drive.jpeg',
929 'is_integrated' => self::getFSIntegrationStatus('google_drive_settings'),
930 'description' => __('A cloud storage service by Google for storing, syncing, and sharing files.', 'fluent-support'),
931 'doc_url' => 'https://fluentsupport.com/docs/google-drive-integration/'
932 ],
933 'dropbox' => [
934 'title' => __('Dropbox', 'fluent-support'),
935 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/dropbox.png',
936 'is_integrated' => self::getFSIntegrationStatus('dropbox_settings'),
937 'description' => __('A cloud-based file storage and sharing service that allows users to store files online and sync them across devices.', 'fluent-support'),
938 'doc_url' => 'https://fluentsupport.com/docs/dropbox-integration/',
939 ],
940 'member-press' => [
941 'title' => __('MemberPress', 'fluent-support'),
942 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/member-press.png',
943 'is_integrated' => class_exists('MeprUtils'),
944 'description' => __('A WordPress plugin that enables the creation and management of membership sites, including content access control and subscription billing.', 'fluent-support'),
945 'doc_url' => 'https://fluentsupport.com/docs/memberpress-integration/'
946 ],
947 'google-recaptcha' => [
948 'title' => __('Google reCAPTCHA', 'fluent-support'),
949 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/google-recaptcha.png',
950 'is_integrated' => self::getFSIntegrationStatus('recaptcha_setting'),
951 'description' => __('A security service by Google designed to protect websites from bots and abuse by using challenges to distinguish between human and automated access.', 'fluent-support'),
952 'doc_url' => 'https://fluentsupport.com/docs/google-recaptcha-integration/',
953 ],
954 'fluent-boards' => [
955 'title' => __('FluentBoards', 'fluent-support'),
956 'logo' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/integrations/fluent-boards.png',
957 'is_integrated' => defined('FLUENT_BOARDS'),
958 'description' => __('A project management tool designed to streamline workflows and collaboration through customizable, kanban-style boards.', 'fluent-support'),
959 'doc_url' => '',
960 ],
961 ];
962
963 return $connections;
964 }
965
966 public static function getGlobalSettingsMenu()
967 {
968 $menu = [
969 [
970 'title' => __('Global Settings', 'fluent-support'),
971 'route_name' => 'global_settings',
972 'icon' => 'Document',
973 ],
974 [
975 'title' => __('Ticket Tags', 'fluent-support'),
976 'route_name' => 'tags',
977 'icon' => 'CollectionTag',
978 ],
979 [
980 'title' => __('Ticket Form Config', 'fluent-support'),
981 'route_name' => 'ticket-form-config',
982 'icon' => 'Setting',
983 ],
984 [
985 'title' => __('Custom Fields', 'fluent-support'),
986 'route_name' => 'custom_fields',
987 'icon' => 'Tickets',
988 ],
989 [
990 'title' => __('Products', 'fluent-support'),
991 'route_name' => 'products',
992 'icon' => 'Goods',
993 ],
994 [
995 'title' => __('Support Staff', 'fluent-support'),
996 'route_name' => 'support-staffs',
997 'icon' => 'User',
998 ],
999 [
1000 'title' => __('FluentCRM Integration', 'fluent-support'),
1001 'route_name' => 'fluentcrm_integration',
1002 'icon' => 'Cpu',
1003 ],
1004 [
1005 'title' => __('Incoming Webhook', 'fluent-support'),
1006 'route_name' => 'incoming-webhook',
1007 'icon' => 'Connection',
1008 ],
1009 [
1010 'title' => __('Notification Integrations', 'fluent-support'),
1011 'route_name' => 'integration',
1012 'icon' => 'AlarmClock',
1013 ],
1014 [
1015 'title' => __('File Upload Integrations', 'fluent-support'),
1016 'route_name' => 'upload_integration',
1017 'icon' => 'FolderAdd',
1018 ],
1019 [
1020 'title' => __('Auto Close Settings', 'fluent-support'),
1021 'route_name' => 'auto_close',
1022 'icon' => 'Timer',
1023 ],
1024 [
1025 'title' => __('Ticket Importer', 'fluent-support'),
1026 'route_name' => 'ticket_importer',
1027 'icon' => 'Download',
1028 ],
1029 [
1030 'title' => __('Recaptcha', 'fluent-support'),
1031 'route_name' => 'reCaptcha',
1032 'icon' => 'Key',
1033 ],
1034 [
1035 'title' => __('Integration Statuses', 'fluent-support'),
1036 'route_name' => 'integration_statuses',
1037 'icon' => 'Connection',
1038 ],
1039 [
1040 'title' => __('OpenAI Integration', 'fluent-support'),
1041 'route_name' => 'openai_integration',
1042 'icon' => 'Connection',
1043 ],
1044 ];
1045
1046 if (defined('FLUENT_SUPPORT_PRO_DIR_FILE')) {
1047 $menu[] = [
1048 'title' => __('License Management', 'fluent-support'),
1049 'route_name' => 'license',
1050 'icon' => 'Lock',
1051 ];
1052 }
1053
1054 return apply_filters('fluent_support/settings_menu_items', $menu);
1055 }
1056
1057 public static function getFSIntegrationStatus($connection_name)
1058 {
1059 $integrationMap = [
1060 'slack_settings' => 'slack_settings',
1061 'discord_settings' => 'discord_settings',
1062 'twilio_settings' => 'twilio_settings',
1063 'telegram_settings' => 'telegram_settings',
1064 'google_drive_settings' => 'google_drive_settings',
1065 'dropbox_settings' => 'dropbox_settings',
1066 'recaptcha_setting' => '_fs_recaptcha_settings'
1067 ];
1068
1069 if (array_key_exists($connection_name, $integrationMap)) {
1070 if ($connection_name == 'google_drive_settings' || $connection_name == 'dropbox_settings') {
1071 return self::checkUploadDriverStatus($connection_name);
1072 } elseif ($connection_name == 'recaptcha_setting') {
1073 return self::checkRecaptchaStatus();
1074 } else {
1075 return self::checkNotificationIntegrationStatus($integrationMap[$connection_name]);
1076 }
1077 }
1078
1079 return false;
1080 }
1081
1082 private static function checkNotificationIntegrationStatus($settingName)
1083 {
1084 $settings = self::getIntegrationOption($settingName, null);
1085 if ($settings) {
1086 $status = Arr::get($settings, 'status', false);
1087 return $status ? true : false;
1088 }
1089 return false;
1090 }
1091
1092 private static function checkUploadDriverStatus($settingName)
1093 {
1094 $settings = self::getIntegrationOption($settingName, null);
1095 if ($settings) {
1096 $enabled = Arr::get($settings, 'status', false);
1097 return $enabled ? true : false;
1098 }
1099 return false;
1100 }
1101
1102 private static function checkRecaptchaStatus()
1103 {
1104 $reCaptchaSettingsData = Meta::where('object_type', '_fs_recaptcha_settings')->first();
1105
1106 if ($reCaptchaSettingsData) {
1107 $settings = static::safeUnserialize($reCaptchaSettingsData->value);
1108 $status = Arr::get($settings, 'is_enabled', false);
1109 return $status == 'true' ? true : false;
1110 }
1111 return false;
1112 }
1113
1114 public static function getAIActivities($data)
1115 {
1116 $page = isset($data['page']) ? intval($data['page']) : 1;
1117 $perPage = isset($data['per_page']) ? intval($data['per_page']) : 10;
1118
1119 $activitiesQuery = AIActivityLogs::with([
1120 'person' => function ($query) {
1121 $query->select(['first_name', 'person_type', 'last_name', 'id', 'avatar']);
1122 },
1123 'ticket' => function ($query) {
1124 $query->select(['id', 'title']);
1125 }
1126 ])->latest('id');
1127
1128 $from = sanitize_text_field( Arr::get( $data, 'from', '' ) );
1129 $to = sanitize_text_field( Arr::get( $data, 'to', '') );
1130
1131 if ( $from != $to ) {
1132 $from = $from . ' ' . '00:00:00';
1133 $to = $to . ' ' . '23:59:59';
1134 }
1135
1136 if ( ( !empty($from) && !empty($to) ) && $from == $to ) {
1137 $activitiesQuery->whereDate('created_at', '=', $from);
1138 } elseif (!empty($from) && !empty($to)) {
1139 $activitiesQuery->whereBetween('created_at', [ $from, $to ]);
1140 }
1141
1142 $agentId = intval( Arr::get($data, 'filters.agent_id') );
1143
1144 if ($agentId) {
1145 $activitiesQuery->where('agent_id', $agentId);
1146 }
1147
1148 return $activitiesQuery->paginate($perPage, ['*'], 'page', $page);
1149 }
1150
1151 public static function updateAISettings($settings)
1152 {
1153 $defaults = [
1154 'delete_days' => 14,
1155 'disable_logs' => 'no'
1156 ];
1157
1158 $settings = wp_parse_args($settings, $defaults);
1159 $settings['delete_days'] = (int)$settings['delete_days'];
1160
1161 Helper::updateOption('_ai_activity_settings', $settings);
1162
1163 return [
1164 'message' => __('AI Activity settings has been updated', 'fluent-support')
1165 ];
1166 }
1167
1168 public static function getSettings()
1169 {
1170 $settings = Helper::getOption('_ai_activity_settings', []);
1171
1172 $defaults = [
1173 'delete_days' => 14,
1174 'disable_logs' => 'no'
1175 ];
1176
1177 $settings = wp_parse_args($settings, $defaults);
1178
1179 if (! $settings ) throw new \Exception('No activity settings found');
1180
1181 return [
1182 'ai_activity_settings' => $settings
1183 ];
1184 }
1185
1186 public static function getIp($anonymize = false)
1187 {
1188 static $ipAddress;
1189
1190 if ($ipAddress) {
1191 return $ipAddress;
1192 }
1193
1194 if (empty($_SERVER['REMOTE_ADDR'])) {
1195 // It's a local cli request
1196 return '127.0.0.1';
1197 }
1198
1199 $ipAddress = '';
1200 $remoteAddr = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '';
1201
1202 if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
1203 //If it's a valid Cloudflare request
1204 if (self::isCfIp($remoteAddr)) {
1205 //Use the CF-Connecting-IP header.
1206 $ipAddress = sanitize_text_field(wp_unslash($_SERVER['HTTP_CF_CONNECTING_IP']));
1207 } else {
1208 //If it isn't valid, then use REMOTE_ADDR.
1209 $ipAddress = $remoteAddr;
1210 }
1211 } else if ($remoteAddr == '127.0.0.1') {
1212 // most probably it's local reverse proxy
1213 if (isset($_SERVER["HTTP_CLIENT_IP"])) {
1214 $ipAddress = sanitize_text_field(wp_unslash($_SERVER["HTTP_CLIENT_IP"]));
1215 } else if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
1216 $ipAddress = (string)rest_is_ip_address(trim(current(preg_split('/,/', sanitize_text_field(wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR']))))));
1217 }
1218 }
1219
1220 if (!$ipAddress) {
1221 $ipAddress = $remoteAddr;
1222 }
1223
1224 $ipAddress = preg_replace('/^(\d+\.\d+\.\d+\.\d+):\d+$/', '\1', $ipAddress);
1225
1226 $ipAddress = apply_filters('fluent_auth/user_ip', $ipAddress);
1227
1228 if ($anonymize) {
1229 return wp_privacy_anonymize_ip($ipAddress);
1230 }
1231
1232 $ipAddress = sanitize_text_field(wp_unslash($ipAddress));
1233
1234 return $ipAddress;
1235 }
1236
1237 public static function isCfIp($ip = '')
1238 {
1239 if (!$ip) {
1240 $ip = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '';
1241 }
1242 $cloudflareIPRanges = array(
1243 '103.21.244.0/22',
1244 '103.22.200.0/22',
1245 '103.31.4.0/22',
1246 '104.16.0.0/13',
1247 '104.24.0.0/14',
1248 '108.162.192.0/18',
1249 '131.0.72.0/22',
1250 '141.101.64.0/18',
1251 '162.158.0.0/15',
1252 '172.64.0.0/13',
1253 '173.245.48.0/20',
1254 '188.114.96.0/20',
1255 '190.93.240.0/20',
1256 '197.234.240.0/22',
1257 '198.41.128.0/17'
1258 );
1259 $validCFRequest = false;
1260 //Make sure that the request came via Cloudflare.
1261 foreach ($cloudflareIPRanges as $range) {
1262 //Use the ip_in_range function from Joomla.
1263 if (self::ipInRange($ip, $range)) {
1264 //IP is valid. Belongs to Cloudflare.
1265 return true;
1266 }
1267 }
1268
1269 return false;
1270 }
1271
1272 private static function ipInRange($ip, $range)
1273 {
1274 if (strpos($range, '/') !== false) {
1275 // $range is in IP/NETMASK format
1276 list($range, $netmask) = explode('/', $range, 2);
1277 if (strpos($netmask, '.') !== false) {
1278 // $netmask is a 255.255.0.0 format
1279 $netmask = str_replace('*', '0', $netmask);
1280 $netmask_dec = ip2long($netmask);
1281 return ((ip2long($ip) & $netmask_dec) == (ip2long($range) & $netmask_dec));
1282 } else {
1283 // $netmask is a CIDR size block
1284 // fix the range argument
1285 $x = explode('.', $range);
1286 while (count($x) < 4) $x[] = '0';
1287 list($a, $b, $c, $d) = $x;
1288 $range = sprintf("%u.%u.%u.%u", empty($a) ? '0' : $a, empty($b) ? '0' : $b, empty($c) ? '0' : $c, empty($d) ? '0' : $d);
1289 $range_dec = ip2long($range);
1290 $ip_dec = ip2long($ip);
1291
1292 # Strategy 1 - Create the netmask with 'netmask' 1s and then fill it to 32 with 0s
1293 #$netmask_dec = bindec(str_pad('', $netmask, '1') . str_pad('', 32-$netmask, '0'));
1294
1295 # Strategy 2 - Use math to create it
1296 $wildcard_dec = pow(2, (32 - $netmask)) - 1;
1297 $netmask_dec = ~$wildcard_dec;
1298
1299 return (($ip_dec & $netmask_dec) == ($range_dec & $netmask_dec));
1300 }
1301 } else {
1302 // range might be 255.255.*.* or 1.2.3.0-1.2.3.255
1303 if (strpos($range, '*') !== false) { // a.b.*.* format
1304 // Just convert to A-B format by setting * to 0 for A and 255 for B
1305 $lower = str_replace('*', '0', $range);
1306 $upper = str_replace('*', '255', $range);
1307 $range = "$lower-$upper";
1308 }
1309
1310 if (strpos($range, '-') !== false) { // A-B format
1311 list($lower, $upper) = explode('-', $range, 2);
1312 $lower_dec = (float)sprintf("%u", ip2long($lower));
1313 $upper_dec = (float)sprintf("%u", ip2long($upper));
1314 $ip_dec = (float)sprintf("%u", ip2long($ip));
1315 return (($ip_dec >= $lower_dec) && ($ip_dec <= $upper_dec));
1316 }
1317 return false;
1318 }
1319 }
1320
1321 public static function loadView($template, $data)
1322 {
1323 extract($data, EXTR_OVERWRITE);
1324
1325 $template = sanitize_file_name($template);
1326
1327 $template = str_replace('.', DIRECTORY_SEPARATOR, $template);
1328
1329 ob_start();
1330 include FLUENT_SUPPORT_PLUGIN_PATH . 'app/Views/emails/' . $template . '.php';
1331 return ob_get_clean();
1332 }
1333
1334 public static function isProductRequired()
1335 {
1336 $settings = Helper::getOption('_ticket_form_settings', []);
1337 return Arr::get($settings, 'product_required_field') === 'yes';
1338 }
1339
1340 public static function getBusinessBox()
1341 {
1342 $businessEmailBoxes = MailBox::select(['id', 'name', 'email', 'mapped_email'])
1343 ->where('box_type', 'email')
1344 ->get();
1345 return $businessEmailBoxes;
1346 }
1347
1348 public static function tempImageMoveUploadDir($ticketId, $contentType, $replyId = null)
1349 {
1350 // Fetch content based on the content type
1351 $content = self::getContentByType($ticketId, $contentType , $replyId);
1352 if (empty($content)) {
1353 return;
1354 }
1355
1356 // Extract image URLs from the content
1357 $imageUrls = self::extractImageUrls($content);
1358 if (empty($imageUrls)) {
1359 return;
1360 }
1361
1362 // Move images to the upload directory and update content
1363 self::moveImagesAndUpdateContent($imageUrls, $ticketId, $contentType, $content, $replyId);
1364 }
1365
1366 /**
1367 * Fetch content based on the content type.
1368 */
1369 private static function getContentByType($ticketId, $contentType, $replyId)
1370 {
1371 if ($contentType == 'ticket-create') {
1372 $ticket = Ticket::find($ticketId);
1373 return $ticket ? $ticket->content : null;
1374 }
1375
1376 $conversation = Conversation::find($replyId);
1377 return $conversation ? $conversation->content : null;
1378 }
1379
1380 /**
1381 * Extract image URLs from the content.
1382 */
1383 private static function extractImageUrls($content)
1384 {
1385 preg_match_all('/<img[^>]+src="([^">]+)"/', $content, $matches);
1386 return $matches[1] ?? [];
1387 }
1388
1389 /**
1390 * Move images to the upload directory and update content.
1391 */
1392 private static function moveImagesAndUpdateContent($imageUrls, $ticketId, $contentType, $content, $replyId)
1393 {
1394 // Get the current site's upload directory
1395 $uploadDirInfo = wp_upload_dir();
1396 $uploadsDir = $uploadDirInfo['basedir'];
1397 $tempDir = $uploadsDir . '/fluent-support/temp_files/';
1398
1399 foreach ($imageUrls as $imageUrl) {
1400 // Build the absolute path for the temporary file
1401 $imageRelativePath = $tempDir . basename($imageUrl);
1402 $absolutePath = $imageRelativePath;
1403
1404 // Move the file to the ticket-specific folder
1405 $newFileInfo = UploadService::copyFileTicketFolder($absolutePath, $ticketId);
1406
1407 // Check if the move was successful
1408 if (empty($newFileInfo['file_path'])) {
1409 continue; // Skip if the file couldn't be copied
1410 }
1411
1412 // Ensure the new URL is correctly constructed
1413 $newFileInfo['url'] = trailingslashit($uploadDirInfo['baseurl']) . 'fluent-support/ticket_' . $ticketId . '/' . basename($newFileInfo['file_path']);
1414
1415 // Replace the old URL with the new one in the content
1416 $content = str_replace($imageUrl, $newFileInfo['url'], $content);
1417 }
1418
1419 // Save the updated content
1420 self::saveUpdatedContent($ticketId, $contentType, $content, $replyId);
1421 }
1422
1423 /**
1424 * Save the updated content based on the content type.
1425 */
1426 private static function saveUpdatedContent($ticketId, $contentType, $content, $replyId)
1427 {
1428 if ($contentType == 'ticket-create') {
1429 $ticket = Ticket::find($ticketId);
1430 if ($ticket) {
1431 $ticket->content = $content;
1432 $ticket->save();
1433 }
1434 } else {
1435 $conversation = Conversation::find($replyId);
1436 if ($conversation) {
1437 $conversation->content = $content;
1438 $conversation->save();
1439 }
1440 }
1441 }
1442
1443 /**
1444 * Safely unserialize data.
1445 *
1446 * @param string $data The serialized data.
1447 * @return mixed The unserialized data or the original data if not serialized.
1448 */
1449 public static function safeUnserialize($data)
1450 {
1451 if (is_serialized($data)) { // Don't attempt to unserialize data that wasn't serialized going in.
1452 return @unserialize(trim($data), ['allowed_classes' => false]);
1453 }
1454
1455 return $data;
1456 }
1457 }
1458