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
fluent-booking / app / Hooks / Scheduler / DailyScheduler.php

DailyScheduler.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 2.5.0, at app/Hooks/Scheduler/DailyScheduler.php

163 lines 5.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBooking\App\Hooks\Scheduler;
4
5 use FluentBooking\App\Models\Booking;
6 use FluentBooking\App\Models\Calendar;
7 use FluentBooking\App\Models\CalendarSlot;
8 use FluentBooking\App\Modules\MCP\Support\WriteGuard;
9 use FluentBooking\App\Services\Helper;
10 use FluentBooking\App\Services\SummaryReportService;
11 use FluentBooking\Framework\Support\Arr;
12
13 class DailyScheduler
14 {
15 public function register()
16 {
17 add_action('fluent_booking/daily_tasks', [$this, 'handleDailyTasks']);
18 }
19
20 public function handleDailyTasks()
21 {
22 // First and in its own try: MCP guard records live in options, so
23 // nothing else expires them, and a failing summary must not block this.
24 try {
25 WriteGuard::purgeExpired();
26 } catch (\Throwable $e) {
27 if (defined('FLUENT_BOOKING_DEBUG') && FLUENT_BOOKING_DEBUG) {
28 error_log('FluentBooking MCP purge failed: ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
29 }
30 }
31
32 SummaryReportService::maybeSendSummary();
33
34 // Last, so a slow remote cannot hold up the summary email.
35 try {
36 $this->maybeSendUsageData();
37 } catch (\Throwable $e) {
38 if (defined('FLUENT_BOOKING_DEBUG') && FLUENT_BOOKING_DEBUG) {
39 error_log('FluentBooking stats sharing failed: ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
40 }
41 }
42 }
43
44 /**
45 * Weekly usage report (counts only), sent only after an admin opted in.
46 *
47 * The maintenance/environment report rides along when due. `_fluent_last_m_run` is
48 * shared by the Fluent plugins, so whichever reports first covers the rest for six days.
49 */
50 private function maybeSendUsageData()
51 {
52 $optin = (array) Helper::getMeta('option', 0, 'optin');
53
54 /**
55 * Whether FluentBooking may send usage data.
56 *
57 * @since 2.4.1
58 *
59 * @param bool $allowed True once an admin has opted in.
60 */
61 if (!apply_filters('fluent_booking/allow_share_stats', Arr::get($optin, 'share_stats') === 'yes')) {
62 return;
63 }
64
65 $lastSent = (int) Arr::get($optin, 'last_sent_at', 0);
66 if ($lastSent && (time() - $lastSent) < 6 * DAY_IN_SECONDS) {
67 return;
68 }
69
70 $report = [
71 'product' => 'fluent-booking',
72 'product_version' => FLUENT_BOOKING_VERSION,
73 // home_url(), not site_url(): the same site key licensing records under.
74 'site_url' => home_url(),
75 'metrics' => $this->getUsageMetrics()
76 ];
77
78 $lastMaintenance = (int) get_option('_fluent_last_m_run');
79 if (!$lastMaintenance || (time() - $lastMaintenance) > 6 * DAY_IN_SECONDS) {
80 $report['maintenance'] = $this->getMaintenanceData(Arr::get($optin, 'email', ''));
81 }
82
83 /**
84 * The usage report sent to WPManageNinja once an admin has opted in.
85 *
86 * @since 2.4.1
87 *
88 * @param array $report product, product_version, site_url, metrics and, when due, maintenance
89 */
90 $report = apply_filters('fluent_booking/share_stats_data', $report);
91
92 $response = wp_remote_post('https://fluentapi.wpmanageninja.com/stats', [
93 'timeout' => 10,
94 'headers' => ['Content-Type' => 'application/json'],
95 'body' => wp_json_encode($report),
96 'cookies' => []
97 ]);
98
99 $code = is_wp_error($response) ? 0 : (int) wp_remote_retrieve_response_code($response);
100 if ($code < 200 || $code >= 300) {
101 return;
102 }
103
104 $optin['last_sent_at'] = time();
105 Helper::updateMeta('option', 0, 'optin', $optin);
106
107 // Any 2xx counts, even `"maintenance": false`: a block the Worker rejected would be
108 // rejected again next week, and it shows in the panel's errors.
109 if (isset($report['maintenance'])) {
110 update_option('_fluent_last_m_run', time());
111 }
112 }
113
114 /**
115 * Counts only, never a person. Keys are snake_case with the window in the name.
116 */
117 private function getUsageMetrics()
118 {
119 $metrics = [
120 'calendars' => Calendar::count(),
121 'events' => CalendarSlot::count(),
122 'bookings' => Booking::count(),
123 'bookings_30d' => Booking::where('created_at', '>=', gmdate('Y-m-d H:i:s', time() - 30 * DAY_IN_SECONDS))->count(),
124 'has_pro' => defined('FLUENT_BOOKING_PRO_DIR_FILE')
125 ];
126
127 $eventTypes = CalendarSlot::groupBy('event_type')
128 ->selectRaw('event_type, COUNT(*) as total')
129 ->pluck('total', 'event_type')
130 ->toArray();
131
132 foreach ($eventTypes as $type => $total) {
133 $type = trim(preg_replace('/[^a-z0-9]+/', '_', strtolower((string) $type)), '_');
134 if ($type) {
135 $metrics['events_' . $type] = (int) $total;
136 }
137 }
138
139 return $metrics;
140 }
141
142 /**
143 * The same fields FluentCRM sends to /plugin-maintenance. The email is only the one the admin
144 * gave when opting in to emails; the stats consent alone never sends an address.
145 */
146 private function getMaintenanceData($optinEmail)
147 {
148 global $wp_version;
149
150 return [
151 'plugin_version' => FLUENT_BOOKING_VERSION,
152 'php_version' => PHP_VERSION,
153 'wp_version' => $wp_version,
154 'plugins' => (array) get_option('active_plugins'),
155 'site_lang' => get_bloginfo('language'),
156 'site_url' => home_url(),
157 'theme' => wp_get_theme()->get('Name'),
158 'site_title' => get_bloginfo('name'),
159 'admin_email' => (string) $optinEmail
160 ];
161 }
162 }
163