PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.1.2
Fluent Support – Helpdesk & Customer Support Ticket System v2.1.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
← All changes | app/Http/Controllers/SettingsController.php +441 -24 1.5.52.1.2 View file →
@@ -4,11 +4,15 @@
4 4
5 5
6 6 use FluentSupport\App\Models\MailBox;
7 7 use FluentSupport\App\Models\Meta;
8 +use FluentSupport\App\Models\Product;
8 9 use FluentSupport\App\Services\EmailNotification\Settings;
9 10 use FluentSupport\App\Services\Helper;
10 -use FluentSupport\Framework\Request\Request;
11 +use FluentSupport\Database\Migrations\AIActivityLogsMigrator;
12 +use FluentSupport\Framework\Http\Request\Request;
13 +use FluentSupport\App\Hooks\Handlers\ReCaptchaHandler;
14 +use FluentSupport\Framework\Support\Arr;
11 15
12 16 /**
13 17 * SettingsController class is responsible for all settings
14 18 * This class is responsible for all request related to settings under global settings tab
@@ -24,9 +28,9 @@
24 28 * @return array|array[]
25 29 */
26 30 public function getSettings(Request $request)
27 31 {
28 - $settingsKey = $request->get('settings_key');
32 + $settingsKey = $request->getSafe('settings_key', 'sanitize_text_field');
29 33
30 34 return (new Settings)->get($settingsKey);
31 35 }
32 36
@@ -39,9 +43,9 @@
39 43 {
40 44 $settings = Meta::where('object_type', 'integration_settings')->get();
41 45 $integrationSettings = [];
42 46 foreach ($settings as $index => $setting) {
43 - $data = maybe_unserialize($setting->value);
47 + $data = Helper::safeUnserialize($setting->value);
44 48 if (!empty($data['status']) && $data && $data['status'] == 'yes') {
45 49 $integrationSettings[] = $setting->key;
46 50 }
47 51 }
@@ -54,11 +58,29 @@
54 58 * @return array
55 59 */
56 60 public function saveSettings(Request $request)
57 61 {
58 - $settingsKey = $request->get('settings_key');
59 - $settings = wp_unslash($request->get('settings'));
62 + $settingsKey = $request->getSafe('settings_key', 'sanitize_text_field');
63 + $settings = wp_unslash($request->get('settings', null));
60 64
65 + // wp-editor fields: sanitize with wp_kses_post (same approach as Fluent Cart)
66 + $htmlFields = ['login_message'];
67 + $htmlValues = [];
68 + if (is_array($settings)) {
69 + foreach ($htmlFields as $field) {
70 + if (isset($settings[$field])) {
71 + $htmlValues[$field] = wp_kses_post($settings[$field]);
72 + }
73 + }
74 + }
75 +
76 + $settings = is_array($settings) ? map_deep($settings, 'sanitize_text_field') : [];
77 +
78 + // Restore HTML fields
79 + foreach ($htmlValues as $field => $value) {
80 + $settings[$field] = $value;
81 + }
82 +
61 83 (new Settings)->save($settingsKey, $settings);
62 84
63 85 return [
64 86 'message' => __('Settings has been updated', 'fluent-support')
@@ -83,9 +105,16 @@
83 105 * @throws \FluentSupport\Framework\Validator\ValidationException
84 106 */
85 107 public function setupPortal(Request $request)
86 108 {
87 - $mailbox = $request->get('mailbox');
109 + $mailbox = $request->get('mailbox', null);
110 + $mailbox = is_array($mailbox) ? [
111 + 'name' => isset($mailbox['name']) ? sanitize_text_field($mailbox['name']) : '',
112 + 'email' => isset($mailbox['email']) ? sanitize_email($mailbox['email']) : '',
113 + 'box_type' => isset($mailbox['box_type']) ? sanitize_key($mailbox['box_type']) : '',
114 + 'is_default' => isset($mailbox['is_default']) ? sanitize_text_field($mailbox['is_default']) : 'yes',
115 + ] : [];
116 +
88 117 $this->validate($mailbox, [
89 118 'name' => 'required',
90 119 'email' => 'required|email',
91 120 'box_type' => 'required'
@@ -90,16 +119,18 @@
90 119 'email' => 'required|email',
91 120 'box_type' => 'required'
92 121 ]);
93 122
94 - $settings = $request->get('global_settings');
123 + $settings = $request->get('global_settings', null);
124 + $settings = is_array($settings) ? [
125 + 'create_portal_page' => isset($settings['create_portal_page']) ? sanitize_text_field($settings['create_portal_page']) : 'no',
126 + 'portal_page_id' => isset($settings['portal_page_id']) ? intval($settings['portal_page_id']) : 0,
127 + ] : [];
95 128
96 129 $createPage = $settings['create_portal_page'] == 'yes';
97 130
98 131 if (!$createPage && empty($settings['portal_page_id'])) {
99 - return $this->sendError([
100 - 'message' => __('Please select a page or enable create page', 'fluent-support')
101 - ]);
132 + Helper::getSafeErrorMessage(new \Exception(__('Please select a page or enable create page', 'fluent-support')));
102 133 }
103 134
104 135 if ($createPage) {
105 136 // we have to create the page
@@ -109,9 +140,9 @@
109 140 'ping_status' => 'close',
110 141 'post_author' => get_current_user_id(),
111 142 'post_title' => __('Support Portal', 'fluent-support'),
112 143 'post_status' => 'publish',
113 - 'post_content' => '[fluent_support_portal]',
144 + 'post_content' => '<!-- wp:shortcode -->[fluent_support_portal]<!-- /wp:shortcode -->',
114 145 'post_type' => 'page'
115 146 )
116 147 );
117 148 } else {
@@ -195,9 +226,9 @@
195 226 'inline_help' => __('Select the default status for new contacts. If you select pending and it\'s a new contact then a double optin email will be sent', 'fluent-support')
196 227 ],
197 228 'assigned_list' => [
198 229 'type' => 'input-options',
199 - 'label' => __('Add to FluentCRM list (optional)', 'fluent-crm'),
230 + 'label' => __('Add to FluentCRM list (optional)', 'fluent-support'),
200 231 'options' => \FluentCrm\App\Models\Lists::select(['id', 'title'])->orderBy('title', 'ASC')->get(),
201 232 'dependency' => [
202 233 'depends_on' => 'enabled',
203 234 'operator' => '=',
@@ -206,9 +237,9 @@
206 237 ],
207 238 'assigned_tags' => [
208 239 'type' => 'input-options',
209 240 'multiple' => true,
210 - 'label' => __('Add to Tags', 'fluent-crm'),
241 + 'label' => __('Add to Tags', 'fluent-support'),
211 242 'options' => \FluentCrm\App\Models\Tag::select(['id', 'title'])->orderBy('title', 'ASC')->get(),
212 243 'dependency' => [
213 244 'depends_on' => 'enabled',
214 245 'operator' => '=',
@@ -231,23 +262,22 @@
231 262 ];
232 263
233 264 }
234 265
235 -
236 266 public function setupInstallation(Request $request)
237 267 {
238 - $installFluentForm = $request->get('install_fluentform', 'no');
268 + $installFluentForm = $request->getSafe('install_fluentform', 'sanitize_text_field', 'no');
239 269
240 270 if ($installFluentForm == 'yes' && !defined('FLUENTFORM')) {
241 271 $this->installFluentForm();
242 272 }
243 273
244 - $optinEmail = $request->get('optin_email', 'no');
274 + $optinEmail = $request->getSafe('optin_email', 'sanitize_email', '');
245 275 if ($optinEmail && is_email($optinEmail)) {
246 276 $this->shareEmail($optinEmail);
247 277 }
248 278
249 - $shareEssential = $request->get('share_essentials', 'no');
279 + $shareEssential = $request->getSafe('share_essentials', 'sanitize_text_field', 'no');
250 280 if ($shareEssential == 'yes') {
251 281 Helper::updateOption('_share_essential', $shareEssential);
252 282 }
253 283
@@ -256,8 +286,214 @@
256 286 ]);
257 287
258 288 }
259 289
290 + public function saveReCaptchaSettings(Request $request)
291 + {
292 + $data = $request->get('reCaptcha');
293 +
294 + if (is_string($data) && 'clear-reCaptcha-settings' === sanitize_text_field($data)) {
295 + if (Meta::where('object_type', '_fs_recaptcha_settings')->delete()) {
296 + return $this->sendSuccess([
297 + 'message' => __('Your reCAPTCHA settings deleted successfully.', 'fluent-support'),
298 + ]);
299 + }
300 +
301 + return $this->sendError([
302 + 'message' => __('Unable to delete reCAPTCHA settings, try again', 'fluent-support'),
303 + ]);
304 + }
305 +
306 + if (!is_array($data)) {
307 + return $this->sendError([
308 + 'message' => __('Invalid reCAPTCHA data.', 'fluent-support'),
309 + ]);
310 + }
311 +
312 + $reCaptchaData = [
313 + 'reCaptcha_version' => sanitize_text_field($data['reCaptchaVersion'] ?? ''),
314 + 'siteKey' => sanitize_text_field($data['siteKey'] ?? ''),
315 + 'secretKey' => sanitize_text_field($data['secretKey'] ?? ''),
316 + 'formContainingReCaptcha' => array_map('sanitize_text_field', (array) ($data['formContainingReCaptcha'] ?? [])),
317 + 'is_enabled' => sanitize_text_field($data['reCaptchaEnabled'] ?? 'no'),
318 + ];
319 +
320 + $previousValue = Meta::where('object_type', '_fs_recaptcha_settings')->first();
321 +
322 + if ($previousValue === $reCaptchaData) {
323 + return $this->sendError([
324 + 'message' => __('Your recaptcha details are already saved.', 'fluent-support'),
325 + ]);
326 + }
327 +
328 + $captchaResponse = sanitize_text_field($data['captchaResponse'] ?? '');
329 +
330 + if ($captchaResponse) {
331 + $verifyReCaptcha = ReCaptchaHandler::validateRecaptcha($captchaResponse, $reCaptchaData['secretKey'], $reCaptchaData['reCaptcha_version']);
332 +
333 + if (!$verifyReCaptcha) {
334 + return $this->sendError([
335 + 'message' => __('Your reCAPTCHA settings are not valid.', 'fluent-support'),
336 + ]);
337 + }
338 + } elseif (!$previousValue) {
339 + return $this->sendError([
340 + 'message' => __('Please verify reCAPTCHA before saving.', 'fluent-support'),
341 + ]);
342 + }
343 +
344 + if ($previousValue) {
345 + Meta::where('object_type', '_fs_recaptcha_settings')->update([
346 + 'value' => maybe_serialize($reCaptchaData)
347 + ]);
348 + return $this->sendSuccess([
349 + 'message' => __('Your reCAPTCHA settings updated successfully.', 'fluent-support'),
350 + ]);
351 + } else {
352 + Meta::insert([
353 + 'object_type' => '_fs_recaptcha_settings',
354 + 'key' => '_fs_recaptcha_data',
355 + 'value' => maybe_serialize($reCaptchaData)
356 + ]);
357 + }
358 +
359 + return $this->sendSuccess([
360 + 'message' => __('Your reCAPTCHA settings added successfully.', 'fluent-support'),
361 + ]);
362 + }
363 +
364 + public function saveOpenAISettings(Request $request)
365 + {
366 + $data = [
367 + 'api_key' => $request->getSafe('api_key', 'sanitize_text_field', ''),
368 + 'model' => $request->getSafe('model', 'sanitize_text_field', ''),
369 + ];
370 +
371 + $response = Helper::authorizeChatGPTAPIKey($data);
372 +
373 + if (is_wp_error($response)) {
374 + return $this->sendError([
375 + 'message' => __('There was an error verifying the API key.', 'fluent-support'),
376 + ]);
377 + }
378 +
379 + $body = json_decode(wp_remote_retrieve_body($response), true);
380 +
381 + if (isset($body['error'])) {
382 + return $this->sendError([
383 + 'message' => __('Invalid API key. Please provide a valid ChatGPT API key.', 'fluent-support'),
384 + ]);
385 + }
386 +
387 + try {
388 + $isDataSaved = Helper::saveOpenAIData('_fs_openai_settings', '_fs_openai_data', $data);
389 + if ($isDataSaved) {
390 + AIActivityLogsMigrator::migrate();
391 + }
392 + return $this->sendSuccess([
393 + 'message' => __('OpenAI settings have been successfully saved.', 'fluent-support'),
394 + ]);
395 + } catch (\Exception $e) {
396 + // translators: %s is the error message from the exception
397 + $translatedMessage = __('An error occurred while saving the settings: %s', 'fluent-support');
398 + $errorMessage = sprintf($translatedMessage, Helper::getSafeErrorMessage($e));
399 +
400 + return $this->sendError([
401 + 'message' => $errorMessage,
402 + ]);
403 + }
404 + }
405 +
406 +
407 + public function disconnectOpenAI()
408 + {
409 + $deletedRecords = Meta::where([
410 + 'object_type' => '_fs_openai_settings',
411 + 'key' => '_fs_openai_data',
412 + ])->delete();
413 +
414 + if ($deletedRecords) {
415 + return $this->sendSuccess([
416 + 'message' => __('OpenAI settings have been successfully disconnected.', 'fluent-support'),
417 + ]);
418 + } else {
419 + return $this->sendError([
420 + 'message' => __('Failed to disconnect OpenAI settings. No matching records found or an error occurred.', 'fluent-support'),
421 + ]);
422 + }
423 + }
424 +
425 + public function getOpenAISettings()
426 + {
427 + $modelOptions = $this->getOpenAIModelOptions();
428 + $supportedModels = array_column($modelOptions, 'value');
429 +
430 + $settings = [
431 + 'api_key' => '',
432 + 'model' => 'gpt-5.2',
433 + ];
434 +
435 + $chatGPTSettingsData = Meta::where('object_type', '_fs_openai_settings')->first();
436 + if ($chatGPTSettingsData) {
437 + $settings = Helper::safeUnserialize($chatGPTSettingsData->value);
438 +
439 + if (!empty($settings['model']) && !in_array($settings['model'], $supportedModels, true)) {
440 + $previousModel = $settings['model'];
441 + $settings['model'] = 'gpt-5.2';
442 + Helper::saveOpenAIData('_fs_openai_settings', '_fs_openai_data', $settings);
443 + $settings['previous_model'] = $previousModel;
444 + $settings['model_migrated'] = true;
445 + }
446 + }
447 +
448 + $settings['model_options'] = $modelOptions;
449 +
450 + return $this->sendSuccess($settings);
451 + }
452 +
453 + private function getOpenAIModelOptions()
454 + {
455 + $models = [
456 + ['value' => 'gpt-5.2', 'label' => 'GPT-5.2'],
457 + ['value' => 'gpt-5.2-chat-latest', 'label' => 'GPT-5.2 Chat'],
458 + ['value' => 'gpt-4.1', 'label' => 'GPT-4.1'],
459 + ['value' => 'gpt-4.1-mini', 'label' => 'GPT-4.1 Mini'],
460 + ['value' => 'gpt-4.1-nano', 'label' => 'GPT-4.1 Nano'],
461 + ['value' => 'gpt-4o', 'label' => 'GPT-4o'],
462 + ['value' => 'gpt-4o-mini', 'label' => 'GPT-4o Mini'],
463 + ['value' => 'gpt-4o-2024-08-06', 'label' => 'GPT-4o (2024-08-06)'],
464 + ['value' => 'gpt-4o-2024-05-13', 'label' => 'GPT-4o (2024-05-13)'],
465 + ['value' => 'gpt-4o-mini-2024-07-18', 'label' => 'GPT-4o Mini (2024-07-18)'],
466 + ['value' => 'gpt-4-turbo', 'label' => 'GPT-4 Turbo'],
467 + ['value' => 'gpt-4-turbo-2024-04-09', 'label' => 'GPT-4 Turbo (2024-04-09)'],
468 + ['value' => 'gpt-4-turbo-preview', 'label' => 'GPT-4 Turbo Preview'],
469 + ['value' => 'gpt-4', 'label' => 'GPT-4'],
470 + ['value' => 'gpt-4-0613', 'label' => 'GPT-4 (0613)'],
471 + ['value' => 'gpt-3.5-turbo', 'label' => 'GPT-3.5 Turbo'],
472 + ['value' => 'gpt-3.5-turbo-0125', 'label' => 'GPT-3.5 Turbo (0125)'],
473 + ['value' => 'o3', 'label' => 'o3'],
474 + ['value' => 'o3-mini', 'label' => 'o3-mini'],
475 + ['value' => 'o4-mini', 'label' => 'o4-mini'],
476 + ['value' => 'o1', 'label' => 'o1'],
477 + ['value' => 'gpt-4-0314', 'label' => 'GPT-4 (0314) - Deprecated soon'],
478 + ['value' => 'gpt-4-1106-preview', 'label' => 'GPT-4 (1106 Preview) - Deprecated soon'],
479 + ['value' => 'gpt-4-0125-preview', 'label' => 'GPT-4 (0125 Preview) - Deprecated soon'],
480 + ];
481 +
482 + return apply_filters('fluent_support/supported_openai_models', $models);
483 + }
484 +
485 + public function getReCaptchaSettings()
486 + {
487 + $reCaptchaSettingsData = Meta::where('object_type', '_fs_recaptcha_settings')->first();
488 + if ($reCaptchaSettingsData) {
489 + $settings = Helper::safeUnserialize($reCaptchaSettingsData->value);
490 + return $this->sendSuccess($settings);
491 + }
492 +
493 + return [];
494 + }
495 +
260 496 private function shareEmail($optinEmail)
261 497 {
262 498 $user = get_user_by('ID', get_current_user_id());
263 499 $data = [
@@ -306,9 +542,9 @@
306 542 }
307 543
308 544 if (!current_user_can('install_plugins')) {
309 545 return $this->sendError([
310 - 'message' => __('Sorry! you do not have permission to install plugin', 'fluent-crm')
546 + 'message' => __('Sorry! you do not have permission to install plugin', 'fluent-support')
311 547 ]);
312 548 }
313 549
314 550 $plugin_id = 'fluent-crm';
@@ -343,22 +579,22 @@
343 579 }
344 580
345 581 if (!current_user_can('install_plugins')) {
346 582 return $this->sendError([
347 - 'message' => __('Sorry! you do not have permission to install plugin', 'fluent-crm')
583 + 'message' => __('Sorry! you do not have permission to install plugin', 'fluent-support')
348 584 ]);
349 585 }
350 586
351 - $plugin_id = 'fluent-crm';
587 + $plugin_id = 'fluentform';
352 588 $plugin = [
353 - 'name' => 'Fluent CRM',
354 - 'repo-slug' => 'fluent-crm',
355 - 'file' => 'fluent-crm.php',
589 + 'name' => 'Fluent Forms',
590 + 'repo-slug' => 'fluentform',
591 + 'file' => 'fluentform.php',
356 592 ];
357 593
358 594 $this->backgroundInstaller($plugin, $plugin_id);
359 595
360 - if (defined('FLUENTCRM')) {
596 + if (defined('FLUENTFORM')) {
361 597 return [
362 598 'is_installed' => true,
363 599 'message' => __('Fluent Forms plugin has been installed and activated successfully', 'fluent-support')
364 600 ];
@@ -487,7 +723,188 @@
487 723 $path = explode('/', $key);
488 724 $filename = end($path);
489 725 $plugins[$filename] = $key;
490 726 return $plugins;
727 + }
728 +
729 + public function getRemoteUploadSettings(Request $request)
730 + {
731 + $dropBoxConfigured = false;
732 + $googleDriveConfigured = false;
733 + $cloudflareR2Configured = false;
734 + $amazonS3Configured = false;
735 +
736 + if (defined('FLUENTSUPPORTPRO')) {
737 + $dropBoxSettings = Helper::getIntegrationOption('dropbox_settings');
738 + $dropBoxConfigured = $dropBoxSettings && !empty($dropBoxSettings['access_token']);
739 +
740 + $googleDriveSettings = Helper::getIntegrationOption('google_drive_settings');
741 + $googleDriveConfigured = $googleDriveSettings && !empty($googleDriveSettings['access_token']);
742 +
743 + $cloudflareR2Settings = Helper::getIntegrationOption('cloudflare_r2_settings');
744 + $cloudflareR2Configured = $cloudflareR2Settings && !empty($cloudflareR2Settings['secret_access_key']) && Arr::get($cloudflareR2Settings, 'status') == 'yes';
745 +
746 + $amazonS3Settings = Helper::getIntegrationOption('amazon_s3_settings');
747 + $amazonS3Configured = $amazonS3Settings && !empty($amazonS3Settings['secret_access_key']) && Arr::get($amazonS3Settings, 'status') == 'yes';
748 + }
749 +
750 + $drivers = apply_filters('fluent_support/storage_drivers_info', [
751 + 'local' => [
752 + 'title' => 'Default WordPress Storage',
753 + 'is_disabled' => false,
754 + 'is_configured' => true,
755 + 'icon' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/folder.svg',
756 + 'description' => __('Upload and store the files to your WordPress File System Storage.', 'fluent-support')
757 + ],
758 + 'dropbox' => [
759 + 'meta_key' => 'dropbox_settings',
760 + 'title' => 'Dropbox',
761 + 'has_config' => true,
762 + 'is_configured' => $dropBoxConfigured,
763 + 'require_pro' => !defined('FLUENTSUPPORTPRO'),
764 + 'icon' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/dbox.svg',
765 + 'description' => __('Upload and store the files to your Dropbox Storage.', 'fluent-support')
766 + ],
767 + 'google_drive' => [
768 + 'meta_key' => 'google_drive_settings',
769 + 'title' => 'Google Drive',
770 + 'has_config' => true,
771 + 'is_configured' => $googleDriveConfigured,
772 + 'require_pro' => !defined('FLUENTSUPPORTPRO'),
773 + 'upgrade_url' => 'https://fluentsupport.com/pricing',
774 + 'description' => __('Upload and store the files to your Google Drive Storage.', 'fluent-support'),
775 + 'icon' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/drive.svg',
776 + ],
777 + 'cloudflare_r2' => [
778 + 'meta_key' => 'cloudflare_r2_settings',
779 + 'title' => 'Cloudflare R2',
780 + 'has_config' => true,
781 + 'is_configured' => $cloudflareR2Configured,
782 + 'require_pro' => !defined('FLUENTSUPPORTPRO'),
783 + 'upgrade_url' => 'https://fluentsupport.com/pricing',
784 + 'description' => __('Upload and store the files to Cloudflare R2 Storage with zero egress fees.', 'fluent-support'),
785 + 'icon' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/cloudflare-r2.svg',
786 + ],
787 + 'amazon_s3' => [
788 + 'meta_key' => 'amazon_s3_settings',
789 + 'title' => 'Amazon S3',
790 + 'has_config' => true,
791 + 'is_configured' => $amazonS3Configured,
792 + 'require_pro' => !defined('FLUENTSUPPORTPRO'),
793 + 'upgrade_url' => 'https://fluentsupport.com/pricing',
794 + 'description' => __('Upload and store the files to Amazon S3 cloud storage.', 'fluent-support'),
795 + 'icon' => FLUENT_SUPPORT_PLUGIN_URL . 'assets/images/icons/amazon-s3.svg',
796 + ]
797 + ]);
798 +
799 + return [
800 + 'drivers' => $drivers,
801 + 'enabled_driver' => Helper::getUploadDriverKey()
802 + ];
803 + }
804 +
805 + public function updateRemoteUploadDriver(Request $request)
806 + {
807 + $driver = $request->getSafe('driver', 'sanitize_text_field');
808 + Helper::updateOption('file_upload_driver', $driver);
809 +
810 + return [
811 + 'message' => 'Upload driver has been updated successfully',
812 + 'driver' => $driver
813 + ];
814 + }
815 +
816 + /**
817 + * getIntegrationLogs method will return the integration logs
818 + * @return array
819 + */
820 + public function integrationStatuses()
821 + {
822 + return [
823 + 'connections' => Helper::getIntegrationStatuses()
824 + ];
825 + }
826 +
827 + public function getSettingsMenu()
828 + {
829 + return Helper::getGlobalSettingsMenu();
830 + }
831 +
832 + public function getFluentBotSettings()
833 + {
834 + $meta = Meta::where([
835 + 'object_type' => 'fluent_bot_settings',
836 + 'object_id' => 1,
837 + 'key' => '_fs_fluent_bot_config'
838 + ])->first();
839 +
840 + $settings = $meta ? Helper::safeUnserialize($meta->value) : [];
841 +
842 + $productItems = Product::all()->map(function ($product) {
843 + return [
844 + 'id' => $product->id,
845 + 'title' => $product->title
846 + ];
847 + })->values()->all();
848 +
849 + return array_merge([
850 + 'generalApiKey' => '',
851 + 'generalBotId' => '',
852 + 'isEnabled' => false,
853 + 'productMappings' => [],
854 + 'products' => $productItems
855 + ], $settings, [
856 + 'products' => $productItems
857 + ]);
858 + }
859 +
860 + public function saveFluentBotSettings(Request $request)
861 + {
862 + $data = [
863 + 'generalBotId' => $request->getSafe('generalBotId', 'sanitize_text_field'),
864 + 'isEnabled' => $request->getSafe('isEnabled', 'rest_sanitize_boolean'),
865 + 'productMappings' => []
866 + ];
867 +
868 + $productMappings = (array) $request->get('productMappings', []);
869 +
870 + foreach ($productMappings as $mapping) {
871 + if (!is_array($mapping)) {
872 + continue;
873 + }
874 +
875 + $data['productMappings'][] = [
876 + 'productId' => intval($mapping['productId'] ?? 0),
877 + 'productTitle' => sanitize_text_field($mapping['productTitle'] ?? ''),
878 + 'botId' => sanitize_text_field($mapping['botId'] ?? ''),
879 + ];
880 + }
881 +
882 + $serialized = maybe_serialize($data);
883 +
884 + $existing = Meta::where([
885 + 'object_type' => 'fluent_bot_settings',
886 + 'object_id' => 1,
887 + 'key' => '_fs_fluent_bot_config'
888 + ])->first();
889 +
890 + if ($existing) {
891 + $existing->update(['value' => $serialized]);
892 + } else {
893 + Meta::create([
894 + 'object_type' => 'fluent_bot_settings',
895 + 'object_id' => 1,
896 + 'key' => '_fs_fluent_bot_config',
897 + 'value' => $serialized
898 + ]);
899 +
900 + AIActivityLogsMigrator::migrate();
901 + }
902 +
903 + return [
904 + 'success' => true,
905 + 'message' => 'Settings saved successfully',
906 + 'data' => $data
907 + ];
491 908 }
492 909
493 910 }