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 +106 -20 2.4.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);
@@ -192,15 +272,16 @@
192 272
193 273 $assets = $app['url.assets'];
194 274
195 275 $portalVars = apply_filters('fluent_booking/admin_portal_vars', [
196 - 'name' => $name,
197 - 'slug' => $slug,
198 - 'menuItems' => $menuItems,
199 - 'settings' => $settingItems,
200 - 'baseUrl' => $baseUrl,
201 - 'logo' => $assets . 'images/logo.svg',
202 - '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') : '',
203 284 ]);
204 285
205 286 do_action('fluent_booking/admin_app_rendering');
206 287
@@ -219,9 +300,9 @@
219 300 return FLUENT_BOOKING_VERSION;
220 301 });
221 302 }
222 303
223 - public function enqueueAssets()
304 + public function enqueueAssets($withApp = true)
224 305 {
225 306 $app = App::getInstance();
226 307
227 308 $isRtl = Helper::fluentbooking_is_rtl();
@@ -325,8 +406,14 @@
325 406 }, 999999);
326 407
327 408 Vite::enqueueStyle('fluent_booing_admin_app', 'admin_css', [], $assetsVersion, 'all');
328 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 +
329 416 do_action($slug . '_loading_app'); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
330 417
331 418 Vite::enqueueScript($slug . '_admin_app', 'admin_app', array('jquery'), $assetsVersion);
332 419
@@ -387,9 +474,8 @@
387 474 $weekSelectTimes = Helper::getWeekSelectTimes();
388 475 $overrideSelectTimes = Helper::getOverrideSelectTimes();
389 476 $statusChangingTimes = Helper::getBookingStatusChangingTimes();
390 477 $defaultTermsAndConditions = Helper::getDefaultTermsAndConditions();
391 - $locationFields = (new CalendarSlot())->getLocationFields();
392 478
393 479 return apply_filters('fluent_booking/admin_vars', [
394 480 'slug' => $slug = $app->config->get('app.slug'),
395 481 'nonce' => wp_create_nonce($slug),
@@ -401,9 +487,8 @@
401 487 'multi_durations' => $multiDurations,
402 488 'buffer_times' => $bufferTimes,
403 489 'slot_intervals' => $slotIntervals,
404 490 'schedule_schema' => $scheduleSchema,
405 - 'location_fields' => $locationFields,
406 491 'custom_field_types' => $customFieldTypes,
407 492 'week_select_times' => $weekSelectTimes,
408 493 'duration_lookup' => $durationLookup,
409 494 'multi_duration_lookup' => $multiDurationLookup,
@@ -435,8 +520,9 @@
435 520 ],
436 521 'has_pro' => defined('FLUENT_BOOKING_PRO_DIR_FILE'),
437 522 'require_upgrade' => defined('FLUENT_BOOKING_PRO_DIR_FILE') && !defined('FLUENT_BOOKING_LITE'),
438 523 'dashboard_notices' => apply_filters('fluent_booking/dashboard_notices', []),
524 + 'optin' => $this->getOptinVars(),
439 525 'payment_methods' => apply_filters('fluent_booking/payment/get_all_methods', []),
440 526 'trans' => TransStrings::getStrings(),
441 527 'date_format' => DateTimeHelper::getDateFormatter(true),
442 528 'time_format' => DateTimeHelper::getTimeFormatter(true),