PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.6
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.6
1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 All 49 releases
← All changes | app/Hooks/Handlers/MenuHandler.php +130 -5 1.3.27 → 1.6.6 View file →
@@ -19,8 +19,10 @@
19 19 use FluentCart\App\Models\AttributeTerm;
20 20 use FluentCart\App\Models\AttributeGroup;
21 21 use FluentCart\App\Models\ShippingMethod;
22 22 use FluentCart\App\Modules\Tax\TaxModule;
23 +use FluentCart\App\Modules\FluentPlayer\FluentPlayerBridge;
24 +use FluentCart\App\Modules\AdminFooter\AdminFooter;
23 25 use FluentCart\App\Helpers\CurrenciesHelper;
24 26 use FluentCart\App\Services\Filter\TaxFilter;
25 27 use FluentCart\App\Services\Filter\OrderFilter;
26 28 use FluentCart\App\Services\Filter\LicenseFilter;
@@ -26,11 +28,18 @@
26 28 use FluentCart\App\Services\Filter\LicenseFilter;
27 29 use FluentCart\App\Services\Filter\LicenseSiteFilter;
28 30 use FluentCart\App\Services\Filter\ProductFilter;
29 31 use FluentCart\App\Services\Filter\CustomerFilter;
32 +use FluentCart\App\Services\Filter\CouponFilter;
33 +use FluentCart\App\Services\Filter\LogFilter;
34 +use FluentCart\App\Services\Filter\OrderBumpFilter;
35 +use FluentCart\App\Modules\Shipping\Services\Filter\ShippingClassFilter;
36 +use FluentCart\App\Modules\Shipping\Services\Filter\ShippingZoneFilter;
30 37 use FluentCart\App\Modules\Integrations\AddOnModule;
38 +use FluentCart\App\Services\DateTime\DayjsFormatter;
31 39 use FluentCart\App\Services\Translations\TransStrings;
32 40 use FluentCart\App\Services\Permission\PermissionManager;
41 +use FluentCart\Database\DataBackfills;
33 42 use FluentCart\App\Modules\PaymentMethods\Core\GatewayManager;
34 43
35 44 class MenuHandler
36 45 {
@@ -43,8 +52,9 @@
43 52
44 53 if ($page == 'fluent-cart') {
45 54
46 55 \FluentCart\App\Events\FirstTimePluginActivation::handle();
56 + (new AdminFooter())->register();
47 57 add_action('admin_enqueue_scripts', function () {
48 58 Vite::enqueueStyle('fluent_cart_admin_app_css',
49 59 'styles/tailwind/style.css',
50 60 );
@@ -55,8 +65,14 @@
55 65
56 66 }
57 67 });
58 68
69 + // Inline dark mode init script for all FluentCart admin pages (prevents flicker)
70 + add_action('admin_head', [$this, 'outputFluentCartDarkModeScript']);
71 +
72 + // Enqueue dark mode toggle JS for taxonomy pages
73 + add_action('admin_head', [$this, 'enqueueDarkModeToggleForTaxonomy']);
74 +
59 75 // Add a post display state for special WC pages.
60 76 add_filter('display_post_states', array($this, 'addDisplayPostStates'), 10, 2);
61 77
62 78 }
@@ -107,8 +123,66 @@
107 123 return $states;
108 124
109 125 }
110 126
127 + public function outputFluentCartDarkModeScript()
128 + {
129 + global $pagenow;
130 + $page = $_GET['page'] ?? '';
131 + $postType = $_GET['post_type'] ?? '';
132 +
133 + // post.php?post=ID&action=edit omits post_type; resolve it from the post ID.
134 + if ($pagenow === 'post.php' && empty($postType)) {
135 + $postId = (int) App::request()->get('post', 0);
136 + $postType = $postId ? (string) get_post_type($postId) : '';
137 + }
138 +
139 + $isFluentCartPage = $page === 'fluent-cart';
140 + $isTaxonomyList = $pagenow === 'edit-tags.php' && $postType === FluentProducts::CPT_NAME;
141 + $isTermEditPage = $pagenow === 'term.php' && $postType === FluentProducts::CPT_NAME;
142 + $isProductEditPage = ($pagenow === 'post.php' || $pagenow === 'edit.php') && $postType === FluentProducts::CPT_NAME;
143 +
144 + if (!$isFluentCartPage && !$isTaxonomyList && !$isTermEditPage && !$isProductEditPage) {
145 + return;
146 + }
147 +
148 + // Runs synchronously in <head> before any paint — applies the dark class and
149 + // data-fct-theme attribute to <html> so logos and the theme icon render correctly
150 + // from the very first frame, with no light-to-dark flash.
151 + echo '<script>' . $this->getDarkModeInitScript() . '</script>';
152 + }
153 +
154 + public function enqueueDarkModeToggleForTaxonomy()
155 + {
156 + global $pagenow;
157 + $postType = $_GET['post_type'] ?? '';
158 +
159 + $isTaxonomyList = $pagenow === 'edit-tags.php' && $postType === FluentProducts::CPT_NAME;
160 + $isTermEditPage = $pagenow === 'term.php' && $postType === FluentProducts::CPT_NAME;
161 +
162 + if ($isTaxonomyList || $isTermEditPage) {
163 + Vite::enqueueScript(
164 + 'fluent_cart_dark_mode_toggle',
165 + 'admin/dark-mode-toggle.js',
166 + [],
167 + FLUENTCART_VERSION,
168 + true
169 + );
170 + }
171 + }
172 +
173 + private function getDarkModeInitScript(): string
174 + {
175 + return "(function(){" .
176 + "var k='fluent_theme_mode',c='fluent_theme_dark'," .
177 + "s=localStorage.getItem(k)||localStorage.getItem('fcart_admin_theme')," .
178 + "t=s==='dark'?'dark':s==='light'?'light':'system'," .
179 + "d=s==='dark'||s==='system:dark'||((!s||s==='system')&&window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches);" .
180 + "document.documentElement.setAttribute('data-fct-theme',t);" .
181 + "if(d)document.documentElement.classList.add(c);" .
182 + "})();";
183 + }
184 +
111 185 public function init()
112 186 {
113 187
114 188 add_action('admin_menu', array($this, 'addAdminMenu'));
@@ -349,8 +423,11 @@
349 423 }
350 424
351 425 public function renderAdminMenu()
352 426 {
427 + if (!apply_filters('fluent_cart/show_admin_top_bar', true)) {
428 + return;
429 + }
353 430 AdminHelper::getAdminMenu(true);
354 431 }
355 432
356 433 public function pushProductNav($post)
@@ -371,13 +448,22 @@
371 448
372 449 do_action('fluent_cart/loading_app', $app);
373 450
374 451 //This should be done before enqueueing the global script.
375 - Vite::enqueueScript($slug . '_admin_app_start', 'admin/bootstrap/app.js', [$slug . '_global_admin_hooks']);
452 + // wp-i18n backs the admin translator (resources/admin/utils/translator/Translator.js).
453 + Vite::enqueueScript($slug . '_admin_app_start', 'admin/bootstrap/app.js', [$slug . '_global_admin_hooks', 'wp-i18n']);
376 454
377 455 //Don't register this script using vite.
378 - wp_enqueue_script($slug . '_global_admin_hooks', Vite::getEnqueuePath('admin/admin_hooks.js'), [], FLUENTCART_VERSION, true);
456 + if (!wp_script_is('wp-hooks', 'registered')) {
457 + $suffix = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
458 + wp_register_script('wp-hooks', includes_url('js/dist/hooks' . $suffix . '.js'), ['wp-polyfill'], FLUENTCART_VERSION, true);
459 + }
460 + if (!wp_script_is('wp-hooks', 'enqueued')) {
461 + wp_enqueue_script('wp-hooks');
462 + }
379 463
464 + wp_enqueue_script($slug . '_global_admin_hooks', Vite::getEnqueuePath('admin/admin_hooks.js'), ['wp-hooks'], FLUENTCART_VERSION, true);
465 +
380 466 $manager = GatewayManager::getInstance();
381 467 $payment_routes = $manager->getRoutes();
382 468 // $payment_routes = apply_filters('fluent_cart/payments/payment_method_settings_routes', []);
383 469
@@ -385,8 +471,9 @@
385 471
386 472 $settings = new StoreSettings();
387 473 $checkoutUrl = add_query_arg(Helper::INSTANT_CHECKOUT_URL_PARAM, '=', $settings->getCheckoutPage());
388 474 $restVars = Helper::getRestInfo();
475 + $hasDataMigrations = current_user_can('manage_options') && DataBackfills::hasPending();
389 476
390 477 $filterOptions = [
391 478 'order_filter_options' => OrderFilter::getTableFilterOptions(),
392 479 'customer_filter_options' => CustomerFilter::getTableFilterOptions(),
@@ -406,10 +493,30 @@
406 493 'license_sites' => ['filters' => Arr::get($filterOptions, 'license_site_filter_options', [])],
407 494 'taxes_table' => ['filters' => Arr::get($filterOptions, 'tax_filter_options', [])],
408 495 'subscriptions' => ['filters' => Arr::get($filterOptions, 'subscription_filter_options', [])],
409 496 'shipping_zone_table' => ['filters' => Arr::get($filterOptions, 'shipping_zone_filter_options', [])],
497 + // The Order Sources report filters orders, so its advanced-filter UI
498 + // reuses the Orders filter vocabulary rather than defining its own.
499 + 'source_report' => ['filters' => Arr::get($filterOptions, 'order_filter_options', [])],
410 500 ];
411 501
502 + // Tables that declare sort options on their filter class but carry no
503 + // filter-options entry above: the Sort popover still needs them, and
504 + // they are what `fluent_cart/{filterName}_table_sorts` feeds.
505 + $sortOnlyTables = [
506 + 'coupon_table' => CouponFilter::class,
507 + 'log_table' => LogFilter::class,
508 + 'order_bump_table' => OrderBumpFilter::class,
509 + 'shipping_class_table' => ShippingClassFilter::class,
510 + 'shipping_zone_table' => ShippingZoneFilter::class,
511 + ];
512 +
513 + foreach ($sortOnlyTables as $tableName => $filterClass) {
514 + if (empty($tableConfig[$tableName]['filters']['sorts'])) {
515 + $tableConfig[$tableName]['filters']['sorts'] = call_user_func([$filterClass, 'getSortOptions']);
516 + }
517 + }
518 +
412 519 $tableConfig = apply_filters('fluent_cart/admin_table_saved_views', $tableConfig, [
413 520 'filterOptions' => $filterOptions
414 521 ]);
415 522
@@ -420,8 +527,9 @@
420 527 $appConfig['version'] = FLUENTCART_VERSION;
421 528 $appConfig['permissions'] = PermissionManager::getUserPermissions();
422 529 $appConfig['isProActive'] = App::isProActive();
423 530 $appConfig['proVersion'] = defined('FLUENTCART_PRO_PLUGIN_VERSION') ? FLUENTCART_PRO_PLUGIN_VERSION : null;
531 + $appConfig['fluentPlayer'] = FluentPlayerBridge::adminAppConfig();
424 532
425 533 $appConfig['logos'] = [
426 534 'dark' => Vite::getAssetUrl('images/logo/logo-full-dark.svg'),
427 535 'light' => Vite::getAssetUrl('images/logo/logo-full.svg'),
@@ -426,9 +534,9 @@
426 534 'dark' => Vite::getAssetUrl('images/logo/logo-full-dark.svg'),
427 535 'light' => Vite::getAssetUrl('images/logo/logo-full.svg'),
428 536 ];
429 537 $appConfig['isModuleTabEnabled'] = $settings->isModuleTabEnabled();
430 - $appConfig['upgrade_url'] = 'https://fluentcart.com/pricing/';
538 + $appConfig['upgrade_url'] = Helper::getUpgradeUrl();
431 539
432 540 $max_upload_size = wp_max_upload_size(); // Returns size in bytes
433 541 $adminLocalizeData = apply_filters('fluent_cart/admin_app_data', [
434 542 'app_config' => $appConfig,
@@ -433,8 +541,9 @@
433 541 $adminLocalizeData = apply_filters('fluent_cart/admin_app_data', [
434 542 'app_config' => $appConfig,
435 543 'slug' => $app->config->get('app.slug'),
436 544 'admin_url' => admin_url('admin.php?page=fluent-cart#/'),
545 + 'wp_admin_url' => rtrim(admin_url(), '/'),
437 546 'frontend_url' => URL::getFrontEndUrl(''),
438 547 'nonce' => wp_create_nonce($slug),
439 548 'rest' => $restVars,
440 549 'brand_logo' => $this->getMenuIcon(),
@@ -459,9 +568,9 @@
459 568 'order_statues' => Status::getOrderStatuses(),
460 569 'editable_order_statues' => Status::getEditableOrderStatuses(),
461 570 'editable_customer_statues' => Status::getEditableCustomerStatuses(),
462 571 'shipping_statuses' => Status::getShippingStatuses(),
463 - 'allow_bulk_payment_status_change' => true,
572 + // Attribute library powering the Advanced Variation feature.
464 573 'variation_attributes' => AttributeGroup::with(['terms'])->get(),
465 574 'variation_terms' => AttributeTerm::query()->get()->keyBy('id'),
466 575 'product_image_base_uri' => Helper::getProductImageBaseUri(),
467 576 'add_on_modules' => AddOnModule::showAddOns(),
@@ -486,14 +595,30 @@
486 595 'site_url' => site_url(),
487 596 'modules_settings' => ModuleSettings::getAllSettings(),
488 597 'purchase_fluent_cart_link' => 'https://fluentcart.com/',
489 598 'admin_notices' => apply_filters('fluent_cart/admin_notices', []),
599 + // POSTs data-backfills/run until done; endpoint requires manage_options
600 + 'has_data_migrations' => $hasDataMigrations,
490 601 'subscription_intervals' => Helper::getAvailableSubscriptionIntervalOptions(),
491 602
492 - 'datei18' => TransStrings::dateTimeStrings(),
603 + 'datei18' => DayjsFormatter::localizedStrings(),
493 604 'el_strings' => TransStrings::elStrings(),
494 605 'wp_locale' => get_locale(),
495 606 'is_full_name_required' => CheckoutFieldsSchema::isFullNameRequired(),
607 + 'business_details' => [
608 + 'company_name' => [
609 + 'enabled' => CheckoutFieldsSchema::isCompanyNameEnabled(),
610 + 'required' => CheckoutFieldsSchema::isCompanyNameRequired(),
611 + ],
612 + 'vat_number' => [
613 + 'enabled' => CheckoutFieldsSchema::isVatNumberEnabled(),
614 + 'required' => CheckoutFieldsSchema::isVatNumberRequired(),
615 + ],
616 + 'legal_registration_id' => [
617 + 'enabled' => CheckoutFieldsSchema::isLegalRegistrationIdEnabled(),
618 + 'required' => CheckoutFieldsSchema::isLegalRegistrationIdRequired(),
619 + ],
620 + ],
496 621 'fct_editor_frame' => '',
497 622 ]);
498 623
499 624 wp_localize_script($slug . '_admin_app_start', 'fluentCartAdminApp', $adminLocalizeData);