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 +250 -88 1.6.0 → 2.5.0 View file →
@@ -2,15 +2,16 @@
2 2
3 3 namespace FluentBooking\App\Hooks\Handlers;
4 4
5 5 use FluentBooking\App\App;
6 +use FluentBooking\App\Vite;
7 +use FluentBooking\App\Models\Booking;
6 8 use FluentBooking\App\Models\Calendar;
7 -use FluentBooking\App\Models\CalendarSlot;
8 -use FluentBooking\App\Models\User;
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,16 +17,50 @@
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 33 }
27 34
35 + /**
36 + * Put the dark class on the document before the page paints.
37 + *
38 + * The theme is chosen in the browser, so the server cannot know it - which means
39 + * without this the first frame is always light, and somebody on dark gets a white flash
40 + * on every page load. This runs in <head>, before wp-admin's own markup, and is the
41 + * only reason it is inline rather than in global_admin.js: a file in the footer is too
42 + * late to matter, and one in the header is still a request the paint would wait on.
43 + *
44 + * `fluent_theme_mode` is the key every Fluent plugin shares (see global_admin.js), and it
45 + * holds a bare `light`, `dark` or `system`, so `system` is resolved here against the
46 + * machine's own preference. <body> does not exist yet, so only <html> is marked;
47 + * global_admin.js puts the same classes on <body> and #wpbody-content once it runs.
48 + *
49 + * @return void
50 + */
51 + public function printThemeClass()
52 + {
53 + $script = <<<'JS'
54 +(function(){try{var s=localStorage.getItem('fluent_theme_mode')||'';
55 +if(!s){s=localStorage.getItem('fcal_color_mode')==='dark'?'dark':'light';}
56 +if(s==='system'){s='system:'+(window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light');}
57 +if(s==='dark'||s==='system:dark'){var e=document.documentElement;e.classList.add('fluent_theme_dark');e.classList.add('dark');}}catch(e){}})();
58 +JS;
59 +
60 + echo '<script id="fluent-booking-theme-class">' . $script . '</script>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
61 + }
62 +
28 63 public function add()
29 64 {
30 65 $capability = PermissionManager::getMenuPermission();
31 66 $menuPriority = 26;
@@ -83,14 +118,94 @@
83 118 add_submenu_page(
84 119 'fluent-booking',
85 120 __('Settings', 'fluent-booking'),
86 121 __('Settings', 'fluent-booking'),
87 - 'manage_options',
122 + $capability,
88 123 'fluent-booking#/settings/general-settings',
89 124 [$this, 'render']
90 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 + }
91 139 }
92 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 +
93 208 public function render()
94 209 {
95 210 if (!as_has_scheduled_action('fluent_booking_five_minutes_tasks')) {
96 211 as_schedule_recurring_action(time(), (60 * 5), 'fluent_booking_five_minutes_tasks', [], 'fluent-booking', true);
@@ -95,9 +210,17 @@
95 210 if (!as_has_scheduled_action('fluent_booking_five_minutes_tasks')) {
96 211 as_schedule_recurring_action(time(), (60 * 5), 'fluent_booking_five_minutes_tasks', [], 'fluent-booking', true);
97 212 }
98 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 +
99 221 $this->changeFooter();
222 +
100 223 $app = App::getInstance();
101 224
102 225 $config = $app->config;
103 226
@@ -126,9 +249,9 @@
126 249 ],
127 250 [
128 251 'key' => 'scheduled-events',
129 252 'label' => __('Bookings', 'fluent-booking'),
130 - 'permalink' => $baseUrl . 'scheduled-events?period=upcoming&author=me',
253 + 'permalink' => $baseUrl . 'scheduled-events',
131 254 ],
132 255 [
133 256 'key' => 'availability',
134 257 'label' => __('Availability', 'fluent-booking'),
@@ -135,10 +258,11 @@
135 258 'permalink' => $baseUrl . 'availability'
136 259 ]
137 260 ];
138 261
139 - if (current_user_can('manage_options')) {
140 - $menuItems[] = [
262 + $settingItems = [];
263 + if (PermissionManager::userCan('manage_all_data')) {
264 + $settingItems = [
141 265 'key' => 'settings',
142 266 'label' => __('Settings', 'fluent-booking'),
143 267 'permalink' => $baseUrl . 'settings/general-settings'
144 268 ];
@@ -143,28 +267,25 @@
143 267 'permalink' => $baseUrl . 'settings/general-settings'
144 268 ];
145 269 }
146 270
147 - if (!defined('FLUENT_BOOKING_PRO_DIR_FILE')) {
148 - $menuItems[] = [
149 - 'key' => 'buy',
150 - 'label' => __('Upgrade to Pro', 'fluent-booking'),
151 - 'permalink' => Helper::getUpgradeUrl()
152 - ];
153 - }
154 -
155 271 $menuItems = apply_filters('fluent_booking/admin_menu_items', $menuItems);
156 272
157 273 $assets = $app['url.assets'];
158 274
159 275 $portalVars = apply_filters('fluent_booking/admin_portal_vars', [
160 - 'name' => $name,
161 - 'slug' => $slug,
162 - 'menuItems' => $menuItems,
163 - 'baseUrl' => $baseUrl,
164 - 'logo' => $assets . 'images/logo.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') : '',
165 284 ]);
166 285
286 + do_action('fluent_booking/admin_app_rendering');
287 +
167 288 $app->view->render('admin.menu', $portalVars);
168 289 }
169 290
170 291 public function changeFooter()
@@ -179,22 +300,20 @@
179 300 return FLUENT_BOOKING_VERSION;
180 301 });
181 302 }
182 303
183 - public function enqueueAssets()
304 + public function enqueueAssets($withApp = true)
184 305 {
185 306 $app = App::getInstance();
186 307
187 308 $isRtl = Helper::fluentbooking_is_rtl();
188 309
189 - $assets = $app['url.assets'];
310 + $slug = $app->config->get('app.slug');
190 311
191 - $slug = $app->config->get('app.slug');
312 + $assetsVersion = Vite::isDev() ? time() : FLUENT_BOOKING_ASSETS_VERSION;
192 313
193 - $adminAppCss = 'admin/admin.css';
194 314 if ($isRtl) {
195 - $adminAppCss = 'admin/admin-rtl.css';
196 - wp_enqueue_style('fluentbooking_admin_rtl', $assets . 'admin/fluentbooking_admin_rtl.css', [], FLUENT_BOOKING_ASSETS_VERSION);
315 + Vite::enqueueStyle('fluentbooking_admin_rtl', 'admin_rtl_css', [], $assetsVersion);
197 316 }
198 317
199 318 add_action('wp_print_scripts', function () {
200 319
@@ -285,28 +404,22 @@
285 404 wp_dequeue_style($wp_styles->registered[$script]->handle);
286 405 }
287 406 }, 999999);
288 407
289 - wp_enqueue_style('fluent_booing_admin_app', $assets . $adminAppCss, [], FLUENT_BOOKING_ASSETS_VERSION, 'all');
408 + Vite::enqueueStyle('fluent_booing_admin_app', 'admin_css', [], $assetsVersion, 'all');
290 409
291 - do_action($slug . '_loading_app');
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 + }
292 415
293 - wp_enqueue_script(
294 - $slug . '_admin_app',
295 - $assets . 'admin/app.js',
296 - array('jquery'),
297 - FLUENT_BOOKING_ASSETS_VERSION,
298 - true
299 - );
416 + do_action($slug . '_loading_app'); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
300 417
301 - wp_enqueue_script(
302 - $slug . '_global_admin',
303 - $assets . 'admin/global_admin.js',
304 - array(),
305 - FLUENT_BOOKING_ASSETS_VERSION,
306 - true
307 - );
418 + Vite::enqueueScript($slug . '_admin_app', 'admin_app', array('jquery'), $assetsVersion);
308 419
420 + Vite::enqueueScript($slug . '_global_admin', 'global_admin', array(), $assetsVersion);
421 +
309 422 if (function_exists('wp_enqueue_editor')) {
310 423 add_filter('user_can_richedit', '__return_true');
311 424 wp_enqueue_editor();
312 425 wp_enqueue_media();
@@ -361,9 +474,8 @@
361 474 $weekSelectTimes = Helper::getWeekSelectTimes();
362 475 $overrideSelectTimes = Helper::getOverrideSelectTimes();
363 476 $statusChangingTimes = Helper::getBookingStatusChangingTimes();
364 477 $defaultTermsAndConditions = Helper::getDefaultTermsAndConditions();
365 - $locationFields = (new CalendarSlot())->getLocationFields();
366 478
367 479 return apply_filters('fluent_booking/admin_vars', [
368 480 'slug' => $slug = $app->config->get('app.slug'),
369 481 'nonce' => wp_create_nonce($slug),
@@ -375,9 +487,8 @@
375 487 'multi_durations' => $multiDurations,
376 488 'buffer_times' => $bufferTimes,
377 489 'slot_intervals' => $slotIntervals,
378 490 'schedule_schema' => $scheduleSchema,
379 - 'location_fields' => $locationFields,
380 491 'custom_field_types' => $customFieldTypes,
381 492 'week_select_times' => $weekSelectTimes,
382 493 'duration_lookup' => $durationLookup,
383 494 'multi_duration_lookup' => $multiDurationLookup,
@@ -387,8 +498,9 @@
387 498 'me' => [
388 499 'id' => $currentUser->ID,
389 500 'calendar_id' => $calendarId,
390 501 'full_name' => $currentUsername,
502 + 'avatar' => get_avatar_url($currentUser->ID, ['size' => 96]),
391 503 'email' => $currentUser->user_email,
392 504 'is_admin' => $hasAllAccess,
393 505 'permissions' => PermissionManager::getUserPermissions($currentUser, false),
394 506 ],
@@ -395,22 +507,35 @@
395 507 'all_hosts' => Calendar::getAllHosts(),
396 508 'is_new' => $isNew,
397 509 'require_slug' => $requireSlug,
398 510 'site_url' => site_url('/'),
511 + 'site_title' => get_bloginfo('name'),
399 512 'upgrade_url' => Helper::getUpgradeUrl(),
400 513 'timezones' => DateTimeHelper::getTimeZones(true),
514 + 'features' => Helper::getFeatures(),
401 515 'supported_features' => apply_filters('fluent_booking/supported_featured', [
402 516 'multi_users' => true
403 517 ]),
404 - 'has_pro' => defined('FLUENT_BOOKING_PRO_DIR_FILE'),
405 - 'require_upgrade' => defined('FLUENT_BOOKING_PRO_DIR_FILE') && !defined('FLUENT_BOOKING_LITE'),
406 - 'dashboard_notices' => apply_filters('fluent_booking/dashboard_notices', []),
407 - 'trans' => TransStrings::getStrings(),
408 - 'date_format' => DateTimeHelper::getDateFormatter(true),
409 - 'time_format' => DateTimeHelper::getTimeFormatter(true),
410 - 'date_time_formatter' => DateTimeHelper::getDateFormatter(true) . ', ' . DateTimeHelper::getTimeFormatter(true),
411 - 'available_date_formats' => DateTimeHelper::getAvailableDateFormats(),
412 - 'admin_url' => admin_url(),
518 + 'i18' => [
519 + 'date_time_config' => DateTimeHelper::getI18nDateTimeConfig(),
520 + ],
521 + 'has_pro' => defined('FLUENT_BOOKING_PRO_DIR_FILE'),
522 + 'require_upgrade' => defined('FLUENT_BOOKING_PRO_DIR_FILE') && !defined('FLUENT_BOOKING_LITE'),
523 + 'dashboard_notices' => apply_filters('fluent_booking/dashboard_notices', []),
524 + 'optin' => $this->getOptinVars(),
525 + 'payment_methods' => apply_filters('fluent_booking/payment/get_all_methods', []),
526 + 'trans' => TransStrings::getStrings(),
527 + 'date_format' => DateTimeHelper::getDateFormatter(true),
528 + 'time_format' => DateTimeHelper::getTimeFormatter(true),
529 + 'date_time_formatter' => DateTimeHelper::getDateFormatter(true) . ', ' . DateTimeHelper::getTimeFormatter(true),
530 + 'available_date_formats' => DateTimeHelper::getAvailableDateFormats(),
531 + 'default_booking_filters' => Helper::getDefaultBookingFilters(),
532 + 'default_paginations' => Helper::getDefaultPaginations(),
533 + 'pref_settings' => Helper::getPrefSettings(),
534 + 'settings_menu_items' => static::settingsMenuItems(),
535 + 'admin_url' => admin_url(),
536 + 'is_rtl' => Helper::fluentbooking_is_rtl(),
537 + 'iframe_html' => Helper::getIframeHtml()
413 538 ]);
414 539 }
415 540
416 541 protected function getRestInfo($app)
@@ -479,9 +604,9 @@
479 604 $urlAssets = $app['url.assets'];
480 605
481 606 return apply_filters('fluent_booking/settings_menu_items', [
482 607 'general_settings' => [
483 - 'title' => __('General Settings', 'fluent-booking'),
608 + 'title' => __('General', 'fluent-booking'),
484 609 'disable' => false,
485 610 'el_icon' => 'Operation',
486 611 'component_type' => 'StandAloneComponent',
487 612 'class' => 'general_settings',
@@ -498,8 +623,44 @@
498 623 'route' => [
499 624 'name' => 'team_members'
500 625 ]
501 626 ],
627 + 'payment_settings' => [
628 + 'title' => __('Payment', 'fluent-booking'),
629 + 'disable' => true,
630 + 'el_icon' => 'Money',
631 + 'component_type' => 'PaymentSettingsComponent',
632 + 'class' => 'payment_settings',
633 + 'route' => [
634 + 'name' => 'global_payment_settings',
635 + ],
636 + 'children' => [
637 + 'payment_settings' => [
638 + 'title' => __('Settings', 'fluent-booking'),
639 + 'disable' => true,
640 + 'route' => [
641 + 'name' => 'global_payment_settings'
642 + ]
643 + ],
644 + 'payment_methods' => [
645 + 'title' => __('Payment Methods', 'fluent-booking'),
646 + 'disable' => true,
647 + 'route' => [
648 + 'name' => 'payment_methods',
649 + 'params' => [
650 + 'settings_key' => 'stripe'
651 + ]
652 + ]
653 + ],
654 + 'payment_coupons' => [
655 + 'title' => __('Coupons', 'fluent-booking'),
656 + 'disable' => true,
657 + 'route' => [
658 + 'name' => 'payment_coupons'
659 + ]
660 + ]
661 + ],
662 + ],
502 663 'google' => [
503 664 'title' => __('Google Calendar / Meet', 'fluent-booking'),
504 665 'disable' => true,
505 666 'icon_url' => $urlAssets . 'images/gg-calendar.svg',
@@ -537,9 +698,9 @@
537 698 ],
538 699 'next_cloud_calendar' => [
539 700 'title' => __('Nextcloud Calendar', 'fluent-booking'),
540 701 'disable' => true,
541 - 'icon_url' => $urlAssets . 'images/Ncloud.svg',
702 + 'svg_icon' => '<svg xmlns="http://www.w3.org/2000/svg" height="800" width="1200" viewBox="-17.0838 -18.65685 148.0596 111.9411"><path d="M57.0328 0C45.2275 0 35.2216 8.0032 32.1204 18.8466c-2.6952-5.7515-8.5359-9.781-15.2633-9.781C7.6053 9.0657 0 16.671 0 25.9228c0 9.2519 7.6052 16.8606 16.857 16.8606 6.7275 0 12.5682-4.0319 15.2635-9.7844 3.1011 10.8442 13.107 18.85 24.9123 18.85 11.718 0 21.6729-7.885 24.8533-18.607 2.745 5.622 8.5135 9.5414 15.1454 9.5414 9.2518 0 16.8605-7.6087 16.8605-16.8606 0-9.2518-7.6087-16.857-16.8605-16.857-6.632 0-12.4003 3.917-15.1454 9.5378C78.7057 7.8825 68.7507 0 57.0328 0zm0 9.8955c8.9116 0 16.0307 7.1156 16.0307 16.0272s-7.119 16.0308-16.0307 16.0308c-8.9116 0-16.0272-7.1192-16.0272-16.0308S48.1212 9.8955 57.0328 9.8955zm-40.1757 9.0657c3.9044 0 6.965 3.0571 6.965 6.9615s-3.0606 6.965-6.965 6.965-6.9616-3.0606-6.9616-6.965 3.0572-6.9615 6.9616-6.9615zm80.1744 0c3.9044 0 6.965 3.0571 6.965 6.9615s-3.0606 6.965-6.965 6.965-6.9616-3.0606-6.9616-6.965 3.0572-6.9615 6.9616-6.9615z" color="currentColor" font-weight="400" font-family="sans-serif" overflow="visible" fill="currentColor"></path><path d="M29.1085 63.7615c2.7752 0 4.3275 1.9756 4.3275 4.939 0 .2822-.2352.5174-.5174.5174h-7.4792c.047 2.6342 1.8816 4.1394 3.9983 4.1394 1.3171 0 2.2579-.5644 2.7283-.9408.2822-.1881.5174-.141.6585.1412l.1411.2352c.1411.2351.094.4703-.1411.6585-.5645.4233-1.7875 1.129-3.4338 1.129-3.0575 0-5.4094-2.2109-5.4094-5.4095.047-3.3868 2.3048-5.4094 5.1272-5.4094zm2.8693 4.4216c-.094-2.1638-1.4112-3.2457-2.9164-3.2457-1.7404 0-3.2456 1.129-3.575 3.2457zm15.584-2.9164v-3.622c0-.3292.1882-.5174.5174-.5174h.3764c.3292 0 .4703.1882.4703.5174v2.446h2.1168c.3292 0 .5174.1882.5174.5174v.1412c0 .3292-.1882.4703-.5174.4703h-2.1168v5.1743c0 2.399 1.4582 2.6812 2.2579 2.7282.4233.047.5644.1411.5644.5174v.2823c0 .3292-.141.4704-.5644.4704-2.2579 0-3.622-1.3642-3.622-3.8102zm10.7718-1.5052c1.7875 0 2.9164.7526 3.4339 1.176.2351.188.2822.4233.047.7055l-.1411.2352c-.1882.2822-.4234.2822-.7056.094-.4704-.3292-1.3641-.9407-2.5871-.9407-2.2579 0-4.0453 1.6934-4.0453 4.1864 0 2.446 1.7874 4.1394 4.0453 4.1394 1.4582 0 2.446-.6585 2.9164-1.0819.2822-.1881.4704-.141.6585.1411l.1411.1882c.1411.2822.0941.4704-.141.7056-.5175.4233-1.7876 1.317-3.6691 1.317-3.0575 0-5.4094-2.2108-5.4094-5.4094.047-3.1986 2.399-5.4564 5.4564-5.4564zm6.2562-3.3398c0-.3292-.1882-.5174.141-.5174h.3764c.3293 0 .8467.1882.8467.5174V71.664c0 1.3171.6115 1.4582 1.0819 1.5053.2352 0 .4233.141.4233.4703v.3293c0 .3293-.1411.5174-.5174.5174-.8467 0-2.352-.2822-2.352-2.54zm9.6429 3.3398c3.0104 0 5.4564 2.3048 5.4564 5.3623 0 3.1046-2.446 5.4565-5.4564 5.4565-3.0105 0-5.4565-2.352-5.4565-5.4565 0-3.0575 2.446-5.3623 5.4565-5.3623zm0 9.5958c2.2108 0 3.9982-1.7875 3.9982-4.2335 0-2.3519-1.7874-4.0923-3.9982-4.0923s-4.0454 1.7875-4.0454 4.0923c.047 2.399 1.8346 4.2335 4.0454 4.2335zm23.4722-9.5958c2.493 0 3.3868 2.0697 3.3868 2.0697h.047s-.047-.3293-.047-.7997v-4.6568c0-.3293-.1412-.5174.1881-.5174h.3763c.3293 0 .8467.1881.8467.5174v13.406c0 .3292-.1411.5174-.4704.5174h-.3292c-.3293 0-.5175-.1411-.5175-.4704v-.7997c0-.3763.0941-.6585.0941-.6585h-.047s-.8938 2.1638-3.575 2.1638c-2.7752 0-4.5157-2.2108-4.5157-5.4095-.094-3.1986 1.8345-5.3623 4.5628-5.3623zm.047 9.5958c1.7404 0 3.3398-1.223 3.3398-4.1864 0-2.1167-1.082-4.1394-3.2927-4.1394-1.8345 0-3.3398 1.5052-3.3398 4.1394.047 2.54 1.3641 4.1864 3.2927 4.1864zm-85.8923.9878h.3763c.3293 0 .5174-.1881.5174-.5174V63.7274c0-1.5993 1.7404-2.7412 3.716-2.7412 1.9757 0 3.7161 1.142 3.7161 2.7412v10.1003c0 .3293.1882.5174.5174.5174h.3763c.3293 0 .4704-.1881.4704-.5174V63.6674c0-2.6812-2.6812-3.9983-5.1272-3.9983-2.3519 0-5.0331 1.317-5.0331 3.9983v10.1603c0 .3293.1411.5174.4704.5174zm78.5073-10.3485h-.3763c-.3293 0-.5175.1882-.5175.5175v5.6916c0 1.5993-1.0348 3.0575-3.0575 3.0575-1.9756 0-3.0575-1.4582-3.0575-3.0575v-5.6916c0-.3293-.1881-.5175-.5174-.5175h-.3763c-.3293 0-.4704.1882-.4704.5175v6.068c0 2.6811 1.9756 3.9982 4.4216 3.9982 2.446 0 4.4217-1.317 4.4217-3.9983v-6.068c.047-.3292-.1412-.5174-.4704-.5174zm-46.5643-.0771c-.1152.0184-.2258.0953-.3317.2214L41.5673 66.41l-1.4249 1.6978-2.1571-2.5715-1.1705-1.3955c-.1058-.1261-.2257-.195-.35-.2058-.1244-.0107-.2533.0366-.3795.1424l-.2884.2416c-.2523.2117-.2392.446-.0276.6982l1.9045 2.2693 1.5793 1.8815-2.3124 2.7553c-.0018.002-.0029.0043-.0046.0064l-1.1668 1.39c-.2117.2523-.188.5178.0643.7295l.2885.2407c.2522.2116.481.1585.6927-.0937l1.9036-2.2693 1.425-1.6977 2.157 2.5715c.0015.0016.0032.0029.0047.0046l1.1668 1.391c.2116.2521.4763.275.7285.0633l.2885-.2416c.2522-.2117.2392-.446.0275-.6983l-1.9045-2.2692-1.5792-1.8815 2.3124-2.7553c.0017-.002.0028-.0044.0046-.0064l1.1668-1.39c.2116-.2523.1879-.5179-.0643-.7295l-.2885-.2416c-.1261-.1058-.2459-.1452-.361-.1268z" fill="currentColor"></path></svg>',
542 703 'component_type' => 'GlobalSettingsComponent',
543 704 'class' => 'configure_nextcloud_calendar',
544 705 'route' => [
545 706 'name' => 'configure-integrations',
@@ -560,9 +721,9 @@
560 721 ],
561 722 'twilio' => [
562 723 'title' => __('SMS by Twilio', 'fluent-booking'),
563 724 'disable' => true,
564 - 'icon_url' => $urlAssets . 'images/tw.svg',
725 + 'svg_icon' => '<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" viewBox="0 0 24 24" id="twilio"><path d="M0,12c0,6.64,5.359,12,12,12s12-5.36,12-12c0-6.641-5.36-12-12-12C5.381-0.008,0.008,5.352,0,11.971V12z M3.199,12c-0.014-4.846,3.904-8.786,8.75-8.801H12c4.847-0.014,8.786,3.904,8.801,8.75V12c0.015,4.847-3.904,8.786-8.75,8.801H12c-4.846,0.015-8.786-3.904-8.801-8.75V12z"></path><path d="M14.959 17.44c1.361 0 2.481-1.12 2.481-2.48 0-1.359-1.12-2.48-2.481-2.479-1.359 0-2.479 1.12-2.479 2.479C12.486 16.326 13.592 17.432 14.959 17.44zM14.959 11.52c1.361 0 2.481-1.12 2.481-2.479 0-1.361-1.12-2.481-2.481-2.481-1.359 0-2.479 1.12-2.479 2.481C12.487 10.407 13.593 11.513 14.959 11.52zM9.042 17.44c1.359 0 2.479-1.12 2.479-2.48 0-1.359-1.121-2.48-2.479-2.479-1.361 0-2.481 1.12-2.481 2.479C6.567 16.327 7.674 17.433 9.042 17.44zM9.042 11.52c1.359 0 2.479-1.12 2.479-2.479 0-1.361-1.121-2.481-2.479-2.481-1.361 0-2.481 1.12-2.481 2.481C6.567 10.408 7.675 11.513 9.042 11.52z"></path></svg>',
565 726 'component_type' => 'GlobalSettingsComponent',
566 727 'class' => 'configure_twilio',
567 728 'route' => [
568 729 'name' => 'configure-integrations',
@@ -570,34 +731,8 @@
570 731 'settings_key' => 'twilio'
571 732 ]
572 733 ]
573 734 ],
574 - 'stripe' => [
575 - 'title' => __('Stripe', 'fluent-booking'),
576 - 'disable' => true,
577 - 'icon_url' => $urlAssets . 'images/payment-methods/stripe.svg',
578 - 'component_type' => 'GlobalSettingsComponent',
579 - 'class' => 'stripe_payment',
580 - 'route' => [
581 - 'name' => 'PaymentSettingsIndex',
582 - 'params' => [
583 - 'settings_key' => 'stripe'
584 - ]
585 - ]
586 - ],
587 - 'paypal' => [
588 - 'title' => __('PayPal', 'fluent-booking'),
589 - 'disable' => true,
590 - 'icon_url' => $urlAssets . 'images/payment-methods/paypal.svg',
591 - 'component_type' => 'GlobalSettingsComponent',
592 - 'class' => 'paypal_payment',
593 - 'route' => [
594 - 'name' => 'PaymentSettingsIndex',
595 - 'params' => [
596 - 'settings_key' => 'paypal'
597 - ]
598 - ]
599 - ],
600 735 'license' => [
601 736 'title' => __('License', 'fluent-booking'),
602 737 'disable' => true,
603 738 'el_icon' => 'Lock',
@@ -605,9 +740,9 @@
605 740 'class' => 'configure_license',
606 741 'route' => [
607 742 'name' => 'license'
608 743 ]
609 - ],
744 + ]
610 745 ]);
611 746 }
612 747
613 748 public static function getEventSettingsMenuItems($event)
@@ -624,9 +759,9 @@
624 759 'event_id' => $event->id
625 760 ]
626 761 ],
627 762 'label' => __('Event Details', 'fluent-booking'),
628 - 'svgIcon' => '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M8 2V5" stroke="#1B2533" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 2V5" stroke="#1B2533" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M7 13H15" stroke="#1B2533" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M7 17H12" stroke="#1B2533" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 3.5C19.33 3.68 21 4.95 21 9.65V15.83C21 19.95 20 22.01 15 22.01H9C4 22.01 3 19.95 3 15.83V9.65C3 4.95 4.67 3.69 8 3.5H16Z" stroke="#1B2533" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/></svg>'
763 + 'svgIcon' => '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M8 2V5" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 2V5" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M7 13H15" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M7 17H12" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 3.5C19.33 3.68 21 4.95 21 9.65V15.83C21 19.95 20 22.01 15 22.01H9C4 22.01 3 19.95 3 15.83V9.65C3 4.95 4.67 3.69 8 3.5H16Z" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/></svg>'
629 764 ],
630 765 'assignment' => [
631 766 'type' => 'route',
632 767 'visible' => false,
@@ -652,9 +787,9 @@
652 787 'event_id' => $event->id
653 788 ]
654 789 ],
655 790 'label' => __('Availability', 'fluent-booking'),
656 - 'svgIcon' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M6.66666 1.66699V4.16699" stroke="#445164" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.3333 1.66699V4.16699" stroke="#445164" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.91666 7.5752H17.0833" stroke="#445164" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M17.5 7.08366V14.167C17.5 16.667 16.25 18.3337 13.3333 18.3337H6.66667C3.75 18.3337 2.5 16.667 2.5 14.167V7.08366C2.5 4.58366 3.75 2.91699 6.66667 2.91699H13.3333C16.25 2.91699 17.5 4.58366 17.5 7.08366Z" stroke="#445164" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.0789 11.4167H13.0864" stroke="#445164" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.0789 13.9167H13.0864" stroke="#445164" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M9.99623 11.4167H10.0037" stroke="#445164" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M9.99623 13.9167H10.0037" stroke="#445164" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.91194 11.4167H6.91942" stroke="#445164" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.91194 13.9167H6.91942" stroke="#445164" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'
791 + 'svgIcon' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M6.66666 1.66699V4.16699" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.3333 1.66699V4.16699" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.91666 7.5752H17.0833" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M17.5 7.08366V14.167C17.5 16.667 16.25 18.3337 13.3333 18.3337H6.66667C3.75 18.3337 2.5 16.667 2.5 14.167V7.08366C2.5 4.58366 3.75 2.91699 6.66667 2.91699H13.3333C16.25 2.91699 17.5 4.58366 17.5 7.08366Z" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.0789 11.4167H13.0864" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.0789 13.9167H13.0864" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M9.99623 11.4167H10.0037" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M9.99623 13.9167H10.0037" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.91194 11.4167H6.91942" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.91194 13.9167H6.91942" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'
657 792 ],
658 793 'limit_settings' => [
659 794 'type' => 'route',
660 795 'visible' => true,
@@ -680,9 +815,9 @@
680 815 'event_id' => $event->id
681 816 ]
682 817 ],
683 818 'label' => __('Question Settings', 'fluent-booking'),
684 - 'svgIcon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8 2V5" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 2V5" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 13.8714V13.6441C12 12.908 12.5061 12.5182 13.0121 12.2043C13.5061 11.9012 14 11.5115 14 10.797C14 9.8011 13.1085 9 12 9C10.8915 9 10 9.8011 10 10.797" stroke="#292D32" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M11.9945 16.4587H12.0053" stroke="#292D32" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 3.5C19.33 3.67504 21 4.91005 21 9.48055V15.4903C21 19.4968 20 21.5 15 21.5H9C4 21.5 3 19.4968 3 15.4903V9.48055C3 4.91005 4.67 3.68476 8 3.5H16Z" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/></svg>'
819 + 'svgIcon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8 2V5" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 2V5" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 13.8714V13.6441C12 12.908 12.5061 12.5182 13.0121 12.2043C13.5061 11.9012 14 11.5115 14 10.797C14 9.8011 13.1085 9 12 9C10.8915 9 10 9.8011 10 10.797" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M11.9945 16.4587H12.0053" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 3.5C19.33 3.67504 21 4.91005 21 9.48055V15.4903C21 19.4968 20 21.5 15 21.5H9C4 21.5 3 19.4968 3 15.4903V9.48055C3 4.91005 4.67 3.68476 8 3.5H16Z" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/></svg>'
685 820 ],
686 821 'email_notification' => [
687 822 'type' => 'route',
688 823 'visible' => true,
@@ -710,8 +845,22 @@
710 845 ],
711 846 'label' => __('SMS Notification', 'fluent-booking'),
712 847 'elIcon' => 'Notification'
713 848 ],
849 + 'recurring_settings' => [
850 + 'type' => 'route',
851 + 'visible' => ($event->isOneToOne() || $event->isGroup()) ? true : false,
852 + 'disable' => true,
853 + 'route' => [
854 + 'name' => 'recurring_settings',
855 + 'params' => [
856 + 'calendar_id' => $event->calendar_id,
857 + 'event_id' => $event->id
858 + ]
859 + ],
860 + 'label' => __('Recurring Settings', 'fluent-booking'),
861 + 'svgIcon' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M6 4H21C21.5523 4 22 4.44772 22 5V12H20V6H6V9L1 5L6 1V4ZM18 20H3C2.44772 20 2 19.5523 2 19V12H4V18H18V15L23 19L18 23V20Z"></path></svg>'
862 + ],
714 863 'advanced_settings' => [
715 864 'type' => 'route',
716 865 'visible' => true,
717 866 'disable' => true,
@@ -783,9 +932,22 @@
783 932 'calendar_id' => $calendar->id
784 933 ]
785 934 ],
786 935 'label' => __('Calendar Settings', 'fluent-booking'),
787 - 'svgIcon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M2 12.8799V11.1199C2 10.0799 2.85 9.21994 3.9 9.21994C5.71 9.21994 6.45 7.93994 5.54 6.36994C5.02 5.46994 5.33 4.29994 6.24 3.77994L7.97 2.78994C8.76 2.31994 9.78 2.59994 10.25 3.38994L10.36 3.57994C11.26 5.14994 12.74 5.14994 13.65 3.57994L13.76 3.38994C14.23 2.59994 15.25 2.31994 16.04 2.78994L17.77 3.77994C18.68 4.29994 18.99 5.46994 18.47 6.36994C17.56 7.93994 18.3 9.21994 20.11 9.21994C21.15 9.21994 22.01 10.0699 22.01 11.1199V12.8799C22.01 13.9199 21.16 14.7799 20.11 14.7799C18.3 14.7799 17.56 16.0599 18.47 17.6299C18.99 18.5399 18.68 19.6999 17.77 20.2199L16.04 21.2099C15.25 21.6799 14.23 21.3999 13.76 20.6099L13.65 20.4199C12.75 18.8499 11.27 18.8499 10.36 20.4199L10.25 20.6099C9.78 21.3999 8.76 21.6799 7.97 21.2099L6.24 20.2199C5.33 19.6999 5.02 18.5299 5.54 17.6299C6.45 16.0599 5.71 14.7799 3.9 14.7799C2.85 14.7799 2 13.9199 2 12.8799Z" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/></svg>'
936 + 'svgIcon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M2 12.8799V11.1199C2 10.0799 2.85 9.21994 3.9 9.21994C5.71 9.21994 6.45 7.93994 5.54 6.36994C5.02 5.46994 5.33 4.29994 6.24 3.77994L7.97 2.78994C8.76 2.31994 9.78 2.59994 10.25 3.38994L10.36 3.57994C11.26 5.14994 12.74 5.14994 13.65 3.57994L13.76 3.38994C14.23 2.59994 15.25 2.31994 16.04 2.78994L17.77 3.77994C18.68 4.29994 18.99 5.46994 18.47 6.36994C17.56 7.93994 18.3 9.21994 20.11 9.21994C21.15 9.21994 22.01 10.0699 22.01 11.1199V12.8799C22.01 13.9199 21.16 14.7799 20.11 14.7799C18.3 14.7799 17.56 16.0599 18.47 17.6299C18.99 18.5399 18.68 19.6999 17.77 20.2199L16.04 21.2099C15.25 21.6799 14.23 21.3999 13.76 20.6099L13.65 20.4199C12.75 18.8499 11.27 18.8499 10.36 20.4199L10.25 20.6099C9.78 21.3999 8.76 21.6799 7.97 21.2099L6.24 20.2199C5.33 19.6999 5.02 18.5299 5.54 17.6299C6.45 16.0599 5.71 14.7799 3.9 14.7799C2.85 14.7799 2 13.9199 2 12.8799Z" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/></svg>'
937 + ],
938 + 'calendar_events' => [
939 + 'type' => 'route',
940 + 'visible' => true,
941 + 'disable' => false,
942 + 'route' => [
943 + 'name' => 'calendar_events',
944 + 'params' => [
945 + 'calendar_id' => $calendar->id
946 + ]
947 + ],
948 + 'label' => __('Event Types', 'fluent-booking'),
949 + 'svgIcon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8 6H21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M8 12H21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M8 18H21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 6H3.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 12H3.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 18H3.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'
788 950 ],
789 951 'remote_calendars' => [
790 952 'type' => 'route',
791 953 'visible' => true,