PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.5.0
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.5.0
2.5.0 2.4.0 2.3.0 2.2.5 2.2.0 2.1.2 2.1.1 trunk 1.10.0 1.10.01 1.10.02 1.5.0 1.5.01 1.5.02 1.5.1 1.5.10 1.5.20 1.5.21 1.5.22 1.5.23 1.5.24 1.5.25 1.6.0 1.7.0 1.7.1 All 34 releases
← All changes | app/Http/Controllers/SettingsController.php +92 -3 1.10.0 → 2.5.0 View file →
@@ -5,8 +5,9 @@
5 5 use FluentBooking\App\App;
6 6 use FluentBooking\App\Services\GlobalModules\GlobalModules;
7 7 use FluentBooking\App\Hooks\Handlers\AdminMenuHandler;
8 8 use FluentBooking\App\Services\Helper;
9 +use FluentBooking\App\Services\CurrenciesHelper;
9 10 use FluentBooking\App\Services\Libs\Countries;
10 11 use FluentBooking\App\Services\OnboardingService;
11 12 use FluentBooking\Framework\Http\Request\Request;
12 13 use FluentBooking\Framework\Support\Arr;
@@ -81,13 +82,14 @@
81 82 'email_footer' => [
82 83 'wrapper_class' => 'fc_full_width fc_mb_0 fc_wp_editor',
83 84 'type' => 'wp-editor-field',
84 85 'label' => __('Email Footer for Booking related emails (Optional)', 'fluent-booking'),
85 - 'inline_help' => __('You may include your business name, address etc here, for example: <br />You have received this email because signed up for an event or made a booking on our website.', 'fluent-booking')
86 + 'inline_help' => __('You may include your business name, address, etc. here. For example: <br />You have received this email because you signed up for an event or made a booking on our website.', 'fluent-booking')
86 87 ]
87 88 ];
88 89
89 90 $settings['all_countries'] = Countries::get();
91 + $settings['share_stats'] = Arr::get((array) Helper::getMeta('option', 0, 'optin'), 'share_stats', '');
90 92
91 93 return apply_filters('fluent_booking/general_settings', $settings);
92 94 }
93 95
@@ -106,11 +108,12 @@
106 108 $santizedSettings['email_footer'] = wp_kses_post($setting['email_footer']);
107 109 }
108 110 $formattedSettings[$settingKey] = $santizedSettings;
109 111 }
110 - $formattedSettings['time_format'] = $request->get('timeFormat');
112 + $formattedSettings['time_format'] = sanitize_text_field($request->get('timeFormat'));
111 113
112 - update_option('_fluent_booking_settings', $formattedSettings, 'no');
114 + // The option also holds keys saved elsewhere, e.g. theme from updateThemeSettings().
115 + update_option('_fluent_booking_settings', array_merge((array) get_option('_fluent_booking_settings', []), $formattedSettings), 'no');
113 116
114 117 return [
115 118 'message' => __('Settings updated successfully', 'fluent-booking'),
116 119 'settings' => $formattedSettings
@@ -132,8 +135,10 @@
132 135 'number_format' => $numberFormat == 'comma_separated' ? 'comma_separated' : 'dot_separated',
133 136 'currency_position' => sanitize_text_field($currencyPosition)
134 137 ], 'no');
135 138
139 + CurrenciesHelper::getGlobalCurrencySettings(true);
140 +
136 141 return [
137 142 'message' => __('Settings updated successfully', 'fluent-booking')
138 143 ];
139 144 }
@@ -255,8 +260,92 @@
255 260 return $this->sendSuccess([
256 261 'message' => __('Settings are saved', 'fluent-booking'),
257 262 'featureModules' => $settings
258 263 ]);
264 + }
265 +
266 + public function updateOptin(Request $request)
267 + {
268 + // Validated before sanitizing: sanitize_email() turns a typo into '', which would pass
269 + // `nullable` and drop the address without telling anyone.
270 + $this->validate([
271 + 'share_stats' => sanitize_text_field($request->get('share_stats', '')),
272 + 'optin_email' => trim((string) $request->get('optin_email', ''))
273 + ], [
274 + 'share_stats' => 'nullable|in:yes,no',
275 + 'optin_email' => 'nullable|email'
276 + ], [
277 + 'optin_email.email' => __('Please enter a valid email address', 'fluent-booking')
278 + ]);
279 +
280 + $data = [
281 + 'share_stats' => sanitize_text_field($request->get('share_stats', '')),
282 + 'optin_email' => sanitize_email($request->get('optin_email', '')),
283 + 'optin_name' => sanitize_text_field($request->get('optin_name', ''))
284 + ];
285 +
286 + // One record: the stats decision, the opt-in email, when the prompt was dismissed and
287 + // when stats were last sent.
288 + $optin = (array) Helper::getMeta('option', 0, 'optin');
289 +
290 + if ($data['share_stats']) {
291 + $optin['share_stats'] = $data['share_stats'];
292 +
293 + // Opting back in sends straight away rather than waiting out the old interval.
294 + if ($data['share_stats'] === 'no') {
295 + unset($optin['last_sent_at']);
296 + }
297 + }
298 +
299 + // Closing the prompt only snoozes it; it says nothing about stats.
300 + // See AdminMenuHandler::showOptinPrompt().
301 + if ($request->get('dismiss') === 'yes') {
302 + $optin['dismissed_at'] = time();
303 + }
304 +
305 + $optinStatus = '';
306 + if ($data['optin_email']) {
307 + // Reported with the usage data instead of the site's admin email.
308 + $optin['email'] = $data['optin_email'];
309 + $optinStatus = $this->subscribeToNewsletter($data['optin_email'], $data['optin_name']);
310 + }
311 +
312 + Helper::updateMeta('option', 0, 'optin', $optin);
313 +
314 + return [
315 + 'message' => __('Settings updated successfully', 'fluent-booking'),
316 + 'share_stats' => Arr::get($optin, 'share_stats', ''),
317 + 'optin_status' => $optinStatus
318 + ];
319 + }
320 +
321 + /**
322 + * Hands the admin's name and email to the licensing Worker, which forwards them to
323 + * fluentbooking.com's FluentCRM. Pro is matched later by site url, so it must be home_url().
324 + *
325 + * @return string 'confirm_email', 'subscribed' or 'queued'
326 + */
327 + private function subscribeToNewsletter($email, $name)
328 + {
329 + $response = wp_remote_post('https://fluentapi.wpmanageninja.com/subscribe', [
330 + 'timeout' => 15,
331 + 'headers' => ['Content-Type' => 'application/json'],
332 + 'cookies' => [],
333 + 'body' => wp_json_encode([
334 + 'product' => 'fluent-booking',
335 + 'product_version' => FLUENT_BOOKING_VERSION,
336 + 'site_url' => home_url(),
337 + 'email' => $email,
338 + 'full_name' => $name,
339 + 'consent_version' => '2026-09-a',
340 + 'locale' => get_user_locale()
341 + ])
342 + ]);
343 +
344 + $body = json_decode(wp_remote_retrieve_body($response), true);
345 + $status = is_array($body) ? Arr::get($body, 'status') : '';
346 +
347 + return in_array($status, ['confirm_email', 'subscribed'], true) ? $status : 'queued';
259 348 }
260 349
261 350 public function installPlugin(Request $request)
262 351 {