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

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

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