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/Hooks/Handlers/AdminMenuHandler.php +113 -20 2.3.0 → 2.5.0 View file →
@@ -3,14 +3,15 @@
3 3 namespace FluentBooking\App\Hooks\Handlers;
4 4
5 5 use FluentBooking\App\App;
6 6 use FluentBooking\App\Vite;
7 +use FluentBooking\App\Models\Booking;
7 8 use FluentBooking\App\Models\Calendar;
8 -use FluentBooking\App\Models\CalendarSlot;
9 9 use FluentBooking\App\Services\DateTimeHelper;
10 10 use FluentBooking\App\Services\Helper;
11 11 use FluentBooking\App\Services\PermissionManager;
12 12 use FluentBooking\App\Services\TransStrings;
13 +use FluentBooking\Framework\Support\Arr;
13 14
14 15 class AdminMenuHandler
15 16 {
16 17 public function register()
@@ -16,21 +17,20 @@
16 17 public function register()
17 18 {
18 19 add_action('admin_menu', [$this, 'add']);
19 20
20 - add_action('admin_enqueue_scripts', function () {
21 - if (!isset($_REQUEST['page']) || $_REQUEST['page'] != 'fluent-booking') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
21 + add_action('admin_enqueue_scripts', function ($hookSuffix) {
22 + if ($hookSuffix !== 'toplevel_page_fluent-booking') {
22 23 return;
23 24 }
25 +
24 26 $this->enqueueAssets();
27 +
28 + add_action('admin_head', function () {
29 + $this->printThemeClass();
30 + }, 1);
31 +
25 32 }, 100);
26 -
27 - add_action('admin_head', function () {
28 - if (!isset($_REQUEST['page']) || $_REQUEST['page'] != 'fluent-booking') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
29 - return;
30 - }
31 - $this->printThemeClass();
32 - }, 1);
33 33 }
34 34
35 35 /**
36 36 * Put the dark class on the document before the page paints.
@@ -122,10 +122,90 @@
122 122 $capability,
123 123 'fluent-booking#/settings/general-settings',
124 124 [$this, 'render']
125 125 );
126 +
127 + if ($this->shouldShowUpgrade()) {
128 + add_submenu_page(
129 + 'fluent-booking',
130 + __('Upgrade to Pro', 'fluent-booking'),
131 + '<span class="fcal_upgrade_pro_menu">' . esc_html__('Upgrade to Pro', 'fluent-booking') . '</span>',
132 + 'manage_options',
133 + Helper::getUpgradeUrl('admin_sidebar_menu')
134 + );
135 +
136 + add_action('admin_head', [$this, 'printUpgradeMenuStyle']);
137 + add_action('admin_footer', [$this, 'printUpgradeMenuScript']);
138 + }
126 139 }
127 140
141 + /**
142 + * The upsell is for whoever can buy the license, so it stays hidden once Pro is loaded
143 + * and from hosts who only reach the app through their own calendars.
144 + *
145 + * @return bool
146 + */
147 + public function shouldShowUpgrade()
148 + {
149 + return !defined('FLUENT_BOOKING_PRO_DIR_FILE') && current_user_can('manage_options');
150 + }
151 +
152 + /**
153 + * Onboarding asks on its final step. The dashboard card waits until the site has a completed
154 + * booking, when FluentBooking has proven useful, and counts one the way the Bookings page's
155 + * "Completed" tab does.
156 + *
157 + * @return array
158 + */
159 + private function getOptinVars()
160 + {
161 + $showPrompt = $this->showOptinPrompt();
162 +
163 + return [
164 + 'share_stats' => Arr::get((array) Helper::getMeta('option', 0, 'optin'), 'share_stats', ''),
165 + 'can_manage' => PermissionManager::userCan('manage_all_data'),
166 + 'show_prompt' => $showPrompt,
167 + 'show_card' => $showPrompt && Booking::applyComputedStatus('completed')->exists(),
168 + ];
169 + }
170 +
171 + /**
172 + * The email opt-in prompt (dashboard card and onboarding) is for free sites that have not
173 + * subscribed yet. Closing it hides it for 30 days.
174 + *
175 + * @return bool
176 + */
177 + private function showOptinPrompt()
178 + {
179 + if (defined('FLUENT_BOOKING_PRO_DIR_FILE') || !PermissionManager::userCan('manage_all_data')) {
180 + return false;
181 + }
182 +
183 + $optin = (array) Helper::getMeta('option', 0, 'optin');
184 +
185 + if (!empty($optin['email'])) {
186 + return false;
187 + }
188 +
189 + return empty($optin['dismissed_at']) || (time() - (int) $optin['dismissed_at']) > 30 * DAY_IN_SECONDS;
190 + }
191 +
192 + public function printUpgradeMenuStyle()
193 + {
194 + echo '<style id="fluent-booking-upgrade-menu">#adminmenu .fcal_upgrade_pro_menu{color:#72aee6;font-weight:500;}</style>';
195 + }
196 +
197 + /**
198 + * The submenu points off-site, and WordPress gives submenu links no target, so it would
199 + * otherwise take the admin away from the dashboard.
200 + *
201 + * @return void
202 + */
203 + public function printUpgradeMenuScript()
204 + {
205 + echo '<script id="fluent-booking-upgrade-menu-js">(function(){var s=document.querySelector("#adminmenu .fcal_upgrade_pro_menu");if(s&&s.parentNode){s.parentNode.setAttribute("target","_blank");s.parentNode.setAttribute("rel","noopener");}})();</script>';
206 + }
207 +
128 208 public function render()
129 209 {
130 210 if (!as_has_scheduled_action('fluent_booking_five_minutes_tasks')) {
131 211 as_schedule_recurring_action(time(), (60 * 5), 'fluent_booking_five_minutes_tasks', [], 'fluent-booking', true);
@@ -130,8 +210,15 @@
130 210 if (!as_has_scheduled_action('fluent_booking_five_minutes_tasks')) {
131 211 as_schedule_recurring_action(time(), (60 * 5), 'fluent_booking_five_minutes_tasks', [], 'fluent-booking', true);
132 212 }
133 213
214 + // The daily task carries the summary report and the MCP record purge.
215 + // A site that never got it — a secondary blog on a network activation,
216 + // or one that lost it — would grow fcal_mcp_g_* rows forever.
217 + if (!as_has_scheduled_action('fluent_booking/daily_tasks')) {
218 + as_schedule_recurring_action(time(), (60 * 60 * 24), 'fluent_booking/daily_tasks', [], 'fluent-booking', true);
219 + }
220 +
134 221 $this->changeFooter();
135 222
136 223 $app = App::getInstance();
137 224
@@ -185,15 +272,16 @@
185 272
186 273 $assets = $app['url.assets'];
187 274
188 275 $portalVars = apply_filters('fluent_booking/admin_portal_vars', [
189 - 'name' => $name,
190 - 'slug' => $slug,
191 - 'menuItems' => $menuItems,
192 - 'settings' => $settingItems,
193 - 'baseUrl' => $baseUrl,
194 - 'logo' => $assets . 'images/logo.svg',
195 - 'dark_logo' => $assets . 'images/logo_dark.svg',
276 + 'name' => $name,
277 + 'slug' => $slug,
278 + 'menuItems' => $menuItems,
279 + 'settings' => $settingItems,
280 + 'baseUrl' => $baseUrl,
281 + 'logo' => $assets . 'images/logo.svg',
282 + 'dark_logo' => $assets . 'images/logo_dark.svg',
283 + 'upgradeUrl' => $this->shouldShowUpgrade() ? Helper::getUpgradeUrl('top_bar') : '',
196 284 ]);
197 285
198 286 do_action('fluent_booking/admin_app_rendering');
199 287
@@ -212,9 +300,9 @@
212 300 return FLUENT_BOOKING_VERSION;
213 301 });
214 302 }
215 303
216 - public function enqueueAssets()
304 + public function enqueueAssets($withApp = true)
217 305 {
218 306 $app = App::getInstance();
219 307
220 308 $isRtl = Helper::fluentbooking_is_rtl();
@@ -318,8 +406,14 @@
318 406 }, 999999);
319 407
320 408 Vite::enqueueStyle('fluent_booing_admin_app', 'admin_css', [], $assetsVersion, 'all');
321 409
410 + // The dashboard payload is per-user and includes every host's email address,
411 + // so the app bundle is only ever emitted for an authenticated, permitted viewer.
412 + if (!$withApp || !get_current_user_id()) {
413 + return;
414 + }
415 +
322 416 do_action($slug . '_loading_app'); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
323 417
324 418 Vite::enqueueScript($slug . '_admin_app', 'admin_app', array('jquery'), $assetsVersion);
325 419
@@ -380,9 +474,8 @@
380 474 $weekSelectTimes = Helper::getWeekSelectTimes();
381 475 $overrideSelectTimes = Helper::getOverrideSelectTimes();
382 476 $statusChangingTimes = Helper::getBookingStatusChangingTimes();
383 477 $defaultTermsAndConditions = Helper::getDefaultTermsAndConditions();
384 - $locationFields = (new CalendarSlot())->getLocationFields();
385 478
386 479 return apply_filters('fluent_booking/admin_vars', [
387 480 'slug' => $slug = $app->config->get('app.slug'),
388 481 'nonce' => wp_create_nonce($slug),
@@ -394,9 +487,8 @@
394 487 'multi_durations' => $multiDurations,
395 488 'buffer_times' => $bufferTimes,
396 489 'slot_intervals' => $slotIntervals,
397 490 'schedule_schema' => $scheduleSchema,
398 - 'location_fields' => $locationFields,
399 491 'custom_field_types' => $customFieldTypes,
400 492 'week_select_times' => $weekSelectTimes,
401 493 'duration_lookup' => $durationLookup,
402 494 'multi_duration_lookup' => $multiDurationLookup,
@@ -428,8 +520,9 @@
428 520 ],
429 521 'has_pro' => defined('FLUENT_BOOKING_PRO_DIR_FILE'),
430 522 'require_upgrade' => defined('FLUENT_BOOKING_PRO_DIR_FILE') && !defined('FLUENT_BOOKING_LITE'),
431 523 'dashboard_notices' => apply_filters('fluent_booking/dashboard_notices', []),
524 + 'optin' => $this->getOptinVars(),
432 525 'payment_methods' => apply_filters('fluent_booking/payment/get_all_methods', []),
433 526 'trans' => TransStrings::getStrings(),
434 527 'date_format' => DateTimeHelper::getDateFormatter(true),
435 528 'time_format' => DateTimeHelper::getTimeFormatter(true),