PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.7.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.7.1
1.7.1 1.7.0 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 All 51 releases
fluent-cart / app / Hooks / Handlers / MenuHandler.php

MenuHandler.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.7.1, at app/Hooks/Handlers/MenuHandler.php

720 lines 30.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Hooks\Handlers;
4
5 use FluentCart\Api\ModuleSettings;
6 use FluentCart\App\App;
7 use FluentCart\App\Services\Renderer\CheckoutFieldsSchema;
8 use FluentCart\App\Vite;
9 use FluentCart\App\Services\URL;
10 use FluentCart\Api\StoreSettings;
11 use FluentCart\Api\PaymentMethods;
12 use FluentCart\App\Helpers\Helper;
13 use FluentCart\App\Helpers\Status;
14 use FluentCart\App\Models\Product;
15 use FluentCart\App\Models\Customer;
16 use FluentCart\Framework\Support\Arr;
17 use FluentCart\App\CPT\FluentProducts;
18 use FluentCart\App\Helpers\AdminHelper;
19 use FluentCart\App\Models\AttributeTerm;
20 use FluentCart\App\Models\AttributeGroup;
21 use FluentCart\App\Models\ShippingMethod;
22 use FluentCart\App\Modules\Tax\TaxModule;
23 use FluentCart\App\Modules\FluentPlayer\FluentPlayerBridge;
24 use FluentCart\App\Modules\AdminFooter\AdminFooter;
25 use FluentCart\App\Helpers\CurrenciesHelper;
26 use FluentCart\App\Services\Filter\TaxFilter;
27 use FluentCart\App\Services\Filter\OrderFilter;
28 use FluentCart\App\Services\Filter\LicenseFilter;
29 use FluentCart\App\Services\Filter\LicenseSiteFilter;
30 use FluentCart\App\Services\Filter\ProductFilter;
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\Services\Filter\ReviewFilter;
36 use FluentCart\App\Modules\Shipping\Services\Filter\ShippingClassFilter;
37 use FluentCart\App\Modules\Shipping\Services\Filter\ShippingZoneFilter;
38 use FluentCart\App\Modules\Integrations\AddOnModule;
39 use FluentCart\App\Services\DateTime\DayjsFormatter;
40 use FluentCart\App\Services\Translations\TransStrings;
41 use FluentCart\App\Services\Permission\PermissionManager;
42 use FluentCart\Database\DataBackfills;
43 use FluentCart\App\Modules\PaymentMethods\Core\GatewayManager;
44
45 class MenuHandler
46 {
47 public function register()
48 {
49 add_action('init', [$this, 'init']);
50
51 add_action('admin_init', function () {
52 $page = $_GET['page'] ?? '';
53
54 if ($page == 'fluent-cart') {
55
56 \FluentCart\App\Events\FirstTimePluginActivation::handle();
57 (new AdminFooter())->register();
58 add_action('admin_enqueue_scripts', function () {
59 Vite::enqueueStyle('fluent_cart_admin_app_css',
60 'styles/tailwind/style.css',
61 );
62 Vite::enqueueScript('fluent_cart_admin_global_js',
63 'admin/global.js',
64 );
65 });
66
67 }
68 });
69
70 // Inline dark mode init script for all FluentCart admin pages (prevents flicker)
71 add_action('admin_head', [$this, 'outputFluentCartDarkModeScript']);
72
73 // Enqueue dark mode toggle JS for taxonomy pages
74 add_action('admin_head', [$this, 'enqueueDarkModeToggleForTaxonomy']);
75
76 // Add a post display state for special WC pages.
77 add_filter('display_post_states', array($this, 'addDisplayPostStates'), 10, 2);
78
79 }
80
81
82 public function addDisplayPostStates($states, $post)
83 {
84 if (!$post || $post->post_type != 'page') {
85 return $states;
86 }
87
88
89 $targetKeys = [
90 'checkout_page_id' => __('Checkout Page', 'fluent-cart'),
91 'cart_page_id' => __('Cart Page', 'fluent-cart'),
92 'receipt_page_id' => __('Receipt Page', 'fluent-cart'),
93 'order_review_page_id' => __('Order Review Page', 'fluent-cart'),
94 'shop_page_id' => __('Shop Page', 'fluent-cart'),
95 'customer_profile_page_id' => __('Customer Profile Page', 'fluent-cart'),
96 ];
97
98 $pagesConfig = (new StoreSettings())->getPagesSettings();
99 $pagesConfig = Arr::only($pagesConfig, array_keys($targetKeys));
100
101 if (!is_array($pagesConfig)) {
102 return $states;
103 }
104 $pagesConfig = array_filter($pagesConfig);
105
106 if (empty($pagesConfig)) {
107 return $states;
108 }
109
110
111 $pagesConfig = array_flip($pagesConfig);
112 $targetKey = Arr::get($pagesConfig, $post->ID);
113
114 if ($targetKey && isset($targetKeys[$targetKey])) {
115
116 $logo = '<svg style="vertical-align: middle; height: 18px; width: 18px;" width="18" height="18" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
117 <rect width="300" height="300" rx="30" fill="white"/>
118 <path d="M136.561 205.944H47.1367L61.1704 173.491C65.2906 163.963 74.6784 157.795 85.0589 157.795H191.584L184.338 174.551C176.098 193.607 157.322 205.944 136.561 205.944Z" fill="#00009F"/>
119 <path d="M210.643 142.439H84.8574L92.1035 125.683C100.344 106.627 119.12 94.2905 139.881 94.2905H248.565L234.531 126.743C230.411 136.271 221.023 142.439 210.643 142.439Z" fill="#00009F"/>
120 </svg>';
121
122 $states['fluent_cart'] = '<span>' . $logo . '<span>' . $targetKeys[$targetKey] . '</span></span>';
123 }
124
125 return $states;
126
127 }
128
129 public function outputFluentCartDarkModeScript()
130 {
131 global $pagenow;
132 $page = $_GET['page'] ?? '';
133 $postType = $_GET['post_type'] ?? '';
134
135 // post.php?post=ID&action=edit omits post_type; resolve it from the post ID.
136 if ($pagenow === 'post.php' && empty($postType)) {
137 $postId = (int) App::request()->get('post', 0);
138 $postType = $postId ? (string) get_post_type($postId) : '';
139 }
140
141 $isFluentCartPage = $page === 'fluent-cart';
142 $isTaxonomyList = $pagenow === 'edit-tags.php' && $postType === FluentProducts::CPT_NAME;
143 $isTermEditPage = $pagenow === 'term.php' && $postType === FluentProducts::CPT_NAME;
144 $isProductEditPage = ($pagenow === 'post.php' || $pagenow === 'edit.php') && $postType === FluentProducts::CPT_NAME;
145
146 if (!$isFluentCartPage && !$isTaxonomyList && !$isTermEditPage && !$isProductEditPage) {
147 return;
148 }
149
150 // Runs synchronously in <head> before any paint — applies the dark class and
151 // data-fct-theme attribute to <html> so logos and the theme icon render correctly
152 // from the very first frame, with no light-to-dark flash.
153 echo '<script>' . $this->getDarkModeInitScript() . '</script>';
154 }
155
156 public function enqueueDarkModeToggleForTaxonomy()
157 {
158 global $pagenow;
159 $postType = $_GET['post_type'] ?? '';
160
161 $isTaxonomyList = $pagenow === 'edit-tags.php' && $postType === FluentProducts::CPT_NAME;
162 $isTermEditPage = $pagenow === 'term.php' && $postType === FluentProducts::CPT_NAME;
163
164 if ($isTaxonomyList || $isTermEditPage) {
165 Vite::enqueueScript(
166 'fluent_cart_dark_mode_toggle',
167 'admin/dark-mode-toggle.js',
168 [],
169 FLUENTCART_VERSION,
170 true
171 );
172 }
173 }
174
175 private function getDarkModeInitScript(): string
176 {
177 return "(function(){" .
178 "var k='fluent_theme_mode',c='fluent_theme_dark'," .
179 "s=localStorage.getItem(k)||localStorage.getItem('fcart_admin_theme')," .
180 "t=s==='dark'?'dark':s==='light'?'light':'system'," .
181 "d=s==='dark'||s==='system:dark'||((!s||s==='system')&&window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches);" .
182 "document.documentElement.setAttribute('data-fct-theme',t);" .
183 "if(d)document.documentElement.classList.add(c);" .
184 "})();";
185 }
186
187 public function init()
188 {
189
190 add_action('admin_menu', array($this, 'addAdminMenu'));
191
192 // The "↳" children under Products stay visible on every page so the
193 // sidebar never changes shape. Pro's "↳ Inventory" still hides itself
194 // with inline CSS (it relied on the admin app to reveal it), so keep
195 // that row visible whichever Pro version is installed.
196 add_action('admin_enqueue_scripts', function () {
197 wp_register_style('fluent-cart-admin-submenu', false);
198 wp_enqueue_style('fluent-cart-admin-submenu');
199 wp_add_inline_style('fluent-cart-admin-submenu', '
200 #adminmenu .toplevel_page_fluent-cart li.fluent_cart_inventory {
201 display: list-item;
202 }
203 ');
204 });
205
206 /*
207 * Disable Gutenberg for Products
208 */
209 add_filter('parent_file', function ($parent_file) {
210 global $submenu_file, $current_screen, $pagenow;
211
212 $postType = $_GET['post_type'] ?? '';
213 if ($postType == FluentProducts::CPT_NAME && $current_screen->post_type == FluentProducts::CPT_NAME) {
214 if ($pagenow == 'post.php') {
215 $submenu_file = 'edit.php?post_type=' . $current_screen->post_type;
216 }
217
218 if ($pagenow == 'edit-tags.php') {
219 $taxonomy = $_GET['taxonomy'] ?? '';
220 $submenu_file = 'edit-tags.php?taxonomy=' . esc_attr($taxonomy) . '&post_type=' . $current_screen->post_type;
221 }
222
223 $parent_file = 'fluent-cart';
224 }
225
226 return $parent_file;
227 });
228
229 add_action('fluent_cart/admin_menu', array($this, 'renderAdminMenu'));
230 // add_action('in_admin_header', array($this, 'maybeRenderAdminMenu'));
231
232 add_action('edit_form_top', array($this, 'pushProductNav'));
233
234 add_action('admin_bar_menu', [$this, 'globalEnqueueAssets'], 10, 1);
235 }
236
237 public function addAdminMenu()
238 {
239 $capability = 'manage_options';
240
241 if (!current_user_can('manage_options') && App::isProActive()) {
242 $capability = PermissionManager::ADMIN_CAP;
243 }
244
245 if (!current_user_can($capability)) {
246 return;
247 }
248
249 global $submenu;
250
251 $adminMenuTitle = apply_filters('fluent_cart/admin_menu_title', 'FluentCart', []);
252
253 add_menu_page(
254 $adminMenuTitle,
255 $adminMenuTitle,
256 $capability,
257 'fluent-cart',
258 [$this, 'renderAdmin'],
259 $this->getMenuIcon(),
260 apply_filters('fluent_cart/admin_menu_position', 3)
261 );
262
263 $submenu['fluent-cart']['dashboard'] = array(
264 __('Dashboard', 'fluent-cart'),
265 $capability,
266 'admin.php?page=fluent-cart#/',
267 '',
268 'fluent_cart_dashboard'
269 );
270
271 if (PermissionManager::hasPermission(['orders/view'])) {
272 $submenu['fluent-cart']['orders'] = array(
273 __('Orders', 'fluent-cart'),
274 $capability,
275 'admin.php?page=fluent-cart#/orders',
276 '',
277 'fluent_cart_orders'
278 );
279 }
280
281 if (PermissionManager::hasPermission(['customers/view', 'customers/manage'])) {
282 $submenu['fluent-cart']['customers'] = array(
283 __('Customers', 'fluent-cart'),
284 $capability,
285 'admin.php?page=fluent-cart#/customers',
286 '',
287 'fluent_cart_customers'
288 );
289 }
290
291 if (PermissionManager::hasPermission(['products/view'])) {
292 $submenu['fluent-cart']['products'] = array(
293 __('Products', 'fluent-cart'),
294 $capability,
295 'admin.php?page=fluent-cart#/products',
296 '',
297 'fluent_cart_products'
298 );
299 }
300
301 if (PermissionManager::hasPermission(['subscriptions/view'])) {
302 $submenu['fluent-cart']['subscriptions'] = array(
303 __('Subscriptions', 'fluent-cart'),
304 $capability,
305 'admin.php?page=fluent-cart#/subscriptions',
306 '',
307 'fluent_cart_subscriptions'
308 );
309 }
310
311 if (PermissionManager::hasPermission(['reports/view'])) {
312 $submenu['fluent-cart']['reports'] = array(
313 __('Reports', 'fluent-cart'),
314 $capability,
315 'admin.php?page=fluent-cart#/reports/overview',
316 '',
317 'fluent_cart_reports'
318 );
319 }
320
321 if (PermissionManager::userCan('is_super_admin')) {
322 $submenu['fluent-cart']['integrations'] = array(
323 __('Integrations', 'fluent-cart'),
324 $capability,
325 'admin.php?page=fluent-cart#/integrations',
326 '',
327 'fluent_cart_integrations'
328 );
329 }
330
331
332 $hasSensitiveAccess = PermissionManager::hasPermission(['store/settings', 'store/sensitive']);
333
334 if ($hasSensitiveAccess) {
335 $submenu['fluent-cart']['settings'] = array(
336 __('Settings', 'fluent-cart'),
337 $capability,
338 'admin.php?page=fluent-cart#/settings/store-settings/',
339 '',
340 'fluent_cart_settings'
341 );
342 }
343
344 if (PermissionManager::hasPermission(['coupons/manage', 'coupons/view'])) {
345 $submenu['fluent-cart']['coupons'] = array(
346 __('Coupons', 'fluent-cart'),
347 $capability,
348 'admin.php?page=fluent-cart#/coupons',
349 '',
350 'fluent_cart_coupons'
351 );
352 }
353
354 if ($hasSensitiveAccess && TaxModule::isTaxEnabled()) {
355 $submenu['fluent-cart']['taxes'] = array(
356 __('Taxes', 'fluent-cart'),
357 $capability,
358 'admin.php?page=fluent-cart#/taxes',
359 '',
360 'fluent_cart_taxes'
361 );
362 }
363
364
365 if (PermissionManager::hasPermission(['products/create', 'products/edit'])) {
366 $taxonomies = get_taxonomies([
367 'object_type' => [FluentProducts::CPT_NAME],
368 'public' => true,
369 ], 'object');
370
371 foreach ($taxonomies as $key => $taxonomy) {
372 $menuUrl = 'edit-tags.php?taxonomy=' . $taxonomy->name . '&post_type=' . FluentProducts::CPT_NAME;
373 $submenu['fluent-cart'][$key] = [
374 $taxonomy->label,
375 $capability,
376 $menuUrl,
377 '',
378 $taxonomy->name
379 ];
380 }
381 }
382
383 if (PermissionManager::userCan('is_super_admin')) {
384 $submenu['fluent-cart']['logs'] = array(
385 __('Logs', 'fluent-cart'),
386 $capability,
387 'admin.php?page=fluent-cart#/logs',
388 '',
389 'fluent_cart_logs'
390 );
391 }
392
393 do_action('fluent_cart/admin_submenu_added', $submenu);
394 }
395
396 public function renderAdmin()
397 {
398 $isCustomDashboardSlugChanged = (new StoreSettings())->getCustomerDashboardPageSlug() != (new StoreSettings())->get('customer_profile_page_slug');
399
400 if ($isCustomDashboardSlugChanged) {
401 $allSettings = (new StoreSettings())->get();
402 (new StoreSettings())->save($allSettings); // we are just resaving to flash the settings cache
403 }
404
405 if (function_exists('wp_enqueue_editor')) {
406 wp_enqueue_editor();
407 wp_tinymce_inline_scripts();
408 wp_enqueue_media();
409 }
410
411 $this->enqueueAssets();
412
413 $config = App::getInstance('config');
414
415 $name = $config->get('app.name');
416
417 $slug = $config->get('app.slug');
418
419 $baseUrl = apply_filters('fluent_connector_base_url', admin_url('admin.php?page=newproject#/'));
420
421 $app = App::getInstance();
422 $assets = $app['url.assets'];
423
424 App::make('view')->render('admin.admin_app', [
425 'name' => $name,
426 'slug' => $slug,
427 'baseUrl' => $baseUrl,
428 'logo' => $assets . 'images/logo.svg',
429 ]);
430 }
431
432 public function maybeRenderAdminMenu()
433 {
434 $postType = $_GET['post_type'] ?? '';
435 if ($postType == FluentProducts::CPT_NAME && is_admin()) {
436 AdminHelper::getAdminMenu(true);
437 AdminHelper::pushGlobalAdminAssets();
438 }
439 }
440
441 public function renderAdminMenu()
442 {
443 if (!apply_filters('fluent_cart/show_admin_top_bar', true)) {
444 return;
445 }
446 AdminHelper::getAdminMenu(true);
447 }
448
449 public function pushProductNav($post)
450 {
451 if (!$post || $post->post_type != FluentProducts::CPT_NAME) {
452 return;
453 }
454
455 AdminHelper::getProductMenu($post->ID, true, 'product_edit');
456 AdminHelper::pushGlobalAdminAssets();
457 }
458
459 public function enqueueAssets()
460 {
461 $app = App::getInstance();
462
463 $slug = $app->config->get('app.slug');
464
465 do_action('fluent_cart/loading_app', $app);
466
467 //This should be done before enqueueing the global script.
468 // wp-i18n backs the admin translator (resources/admin/utils/translator/Translator.js).
469 Vite::enqueueScript($slug . '_admin_app_start', 'admin/bootstrap/app.js', [$slug . '_global_admin_hooks', 'wp-i18n']);
470
471 //Don't register this script using vite.
472 if (!wp_script_is('wp-hooks', 'registered')) {
473 $suffix = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
474 wp_register_script('wp-hooks', includes_url('js/dist/hooks' . $suffix . '.js'), ['wp-polyfill'], FLUENTCART_VERSION, true);
475 }
476 if (!wp_script_is('wp-hooks', 'enqueued')) {
477 wp_enqueue_script('wp-hooks');
478 }
479
480 wp_enqueue_script($slug . '_global_admin_hooks', Vite::getEnqueuePath('admin/admin_hooks.js'), ['wp-hooks'], FLUENTCART_VERSION, true);
481
482 $manager = GatewayManager::getInstance();
483 $payment_routes = $manager->getRoutes();
484 // $payment_routes = apply_filters('fluent_cart/payments/payment_method_settings_routes', []);
485
486 $storage_driver_routes = apply_filters('fluent_cart/storage/storage_driver_settings_routes', [], []);
487
488 $settings = new StoreSettings();
489 $checkoutUrl = add_query_arg(Helper::INSTANT_CHECKOUT_URL_PARAM, '=', $settings->getCheckoutPage());
490 $restVars = Helper::getRestInfo();
491 $hasDataMigrations = current_user_can('manage_options') && DataBackfills::hasPending();
492
493 $filterOptions = [
494 'order_filter_options' => OrderFilter::getTableFilterOptions(),
495 'customer_filter_options' => CustomerFilter::getTableFilterOptions(),
496 'product_filter_options' => ProductFilter::getTableFilterOptions(),
497 'license_filter_options' => LicenseFilter::getTableFilterOptions(),
498 'license_site_filter_options' => LicenseSiteFilter::getTableFilterOptions(),
499 'tax_filter_options' => TaxFilter::getTableFilterOptions(),
500 'review_filter_options' => ReviewFilter::getTableFilterOptions(),
501 ];
502 $filterOptions = apply_filters('fluent_cart/admin_filter_options', $filterOptions, []);
503
504 // Structured table config keyed by table name (used by Table.js base class)
505 $tableConfig = [
506 'order_table' => ['filters' => Arr::get($filterOptions, 'order_filter_options', [])],
507 'customers' => ['filters' => Arr::get($filterOptions, 'customer_filter_options', [])],
508 'product_table' => ['filters' => Arr::get($filterOptions, 'product_filter_options', [])],
509 'licenses' => ['filters' => Arr::get($filterOptions, 'license_filter_options', [])],
510 'license_sites' => ['filters' => Arr::get($filterOptions, 'license_site_filter_options', [])],
511 'taxes_table' => ['filters' => Arr::get($filterOptions, 'tax_filter_options', [])],
512 'subscriptions' => ['filters' => Arr::get($filterOptions, 'subscription_filter_options', [])],
513 'shipping_zone_table' => ['filters' => Arr::get($filterOptions, 'shipping_zone_filter_options', [])],
514 'review_table' => ['filters' => Arr::get($filterOptions, 'review_filter_options', [])],
515 // The Order Sources report filters orders, so its advanced-filter UI
516 // reuses the Orders filter vocabulary rather than defining its own.
517 'source_report' => ['filters' => Arr::get($filterOptions, 'order_filter_options', [])],
518 ];
519
520 // Tables that declare sort options on their filter class but carry no
521 // filter-options entry above: the Sort popover still needs them, and
522 // they are what `fluent_cart/{filterName}_table_sorts` feeds.
523 $sortOnlyTables = [
524 'coupon_table' => CouponFilter::class,
525 'log_table' => LogFilter::class,
526 'order_bump_table' => OrderBumpFilter::class,
527 'shipping_class_table' => ShippingClassFilter::class,
528 'shipping_zone_table' => ShippingZoneFilter::class,
529 ];
530
531 foreach ($sortOnlyTables as $tableName => $filterClass) {
532 if (empty($tableConfig[$tableName]['filters']['sorts'])) {
533 $tableConfig[$tableName]['filters']['sorts'] = call_user_func([$filterClass, 'getSortOptions']);
534 }
535 }
536
537 $tableConfig = apply_filters('fluent_cart/admin_table_saved_views', $tableConfig, [
538 'filterOptions' => $filterOptions
539 ]);
540
541 $currentUser = get_user_by('ID', get_current_user_id());
542
543 // Get app config and merge additional properties into it
544 $appConfig = $app->config->get('app');
545 $appConfig['version'] = FLUENTCART_VERSION;
546 $appConfig['permissions'] = PermissionManager::getUserPermissions();
547 $appConfig['isProActive'] = App::isProActive();
548 $appConfig['proVersion'] = defined('FLUENTCART_PRO_PLUGIN_VERSION') ? FLUENTCART_PRO_PLUGIN_VERSION : null;
549 $appConfig['fluentPlayer'] = FluentPlayerBridge::adminAppConfig();
550
551 $appConfig['logos'] = [
552 'dark' => Vite::getAssetUrl('images/logo/logo-full-dark.svg'),
553 'light' => Vite::getAssetUrl('images/logo/logo-full.svg'),
554 ];
555 $appConfig['isModuleTabEnabled'] = $settings->isModuleTabEnabled();
556 $appConfig['upgrade_url'] = Helper::getUpgradeUrl();
557
558 $max_upload_size = wp_max_upload_size(); // Returns size in bytes
559 $adminLocalizeData = apply_filters('fluent_cart/admin_app_data', [
560 'app_config' => $appConfig,
561 'slug' => $app->config->get('app.slug'),
562 'admin_url' => admin_url('admin.php?page=fluent-cart#/'),
563 'wp_admin_url' => rtrim(admin_url(), '/'),
564 'frontend_url' => URL::getFrontEndUrl(''),
565 'nonce' => wp_create_nonce($slug),
566 'rest' => $restVars,
567 'brand_logo' => $this->getMenuIcon(),
568 'asset_url' => Vite::getAssetUrl(),
569 'me' => [
570 'id' => $currentUser->ID,
571 'full_name' => trim($currentUser->display_name),
572 'email' => $currentUser->user_email,
573 'avatar' => get_avatar_url($currentUser->ID)
574 ],
575 'shop' => Helper::shopConfig(),
576 'product_statuses' => Status::getProductStatuses(),
577 'total_products_count' => Product::query()->count(),
578 'payment_statuses' => Status::getEditableTransactionStatuses(true),
579 'payment_routes' => $payment_routes,
580 'storage_driver_routes' => $storage_driver_routes,
581 'editable_payment_statuses' => Status::getEditableTransactionStatuses(true),
582 'order_statuses' => Status::getOrderStatuses(),
583 'editable_order_statuses' => Status::getEditableOrderStatuses(),
584 'editable_customer_statuses' => Status::getEditableCustomerStatuses(),
585 // Backward compat: keep the old typo'd keys for existing frontend code
586 'order_statues' => Status::getOrderStatuses(),
587 'editable_order_statues' => Status::getEditableOrderStatuses(),
588 'editable_customer_statues' => Status::getEditableCustomerStatuses(),
589 'shipping_statuses' => Status::getShippingStatuses(),
590 // Attribute library powering the Advanced Variation feature.
591 'variation_attributes' => AttributeGroup::with(['terms'])->get(),
592 'variation_terms' => AttributeTerm::query()->get()->keyBy('id'),
593 'product_image_base_uri' => Helper::getProductImageBaseUri(),
594 'add_on_modules' => AddOnModule::showAddOns(),
595 'stock_statuses' => Status::getStockStatuses(),
596 'fulfillment_types' => Helper::getFulfilmentTypes(),
597 'variation_types' => Helper::getVariationTypes(),
598 'trans' => TransStrings::getStrings(),
599 'dashboard_url' => admin_url('admin.php?page=fluent-cart#'),
600 'checkout_url' => $checkoutUrl,
601 'dummy_product_info' => apply_filters('fluent_cart/dummy_product_info', []) ?: [],
602 'currency_signs' => CurrenciesHelper::getCurrencySigns(),
603 'filter_options' => $filterOptions,
604 'table_config' => $tableConfig,
605 'receipt_page_url' => $settings->getReceiptPage(),
606 'payment_logos' => PaymentMethods::getIcons(),
607 'is_admin_bar_showing' => is_admin_bar_showing(),
608 'max_upload_size' => size_format($max_upload_size),
609 'available_shipping_method_count' => ShippingMethod::query()->where('is_enabled', '1')->count(),
610 'is_tax_enabled' => TaxModule::isTaxEnabled(),
611 'eu_vat_county_options' => TaxModule::euVatCountyOptions(),
612 'country_tax_titles' => TaxModule::taxTitleLists(),
613 'site_url' => site_url(),
614 'store_logo' => (new \FluentCart\Api\StoreSettings())->get('store_logo.url', ''),
615 'modules_settings' => ModuleSettings::getAllSettings(),
616 'purchase_fluent_cart_link' => 'https://fluentcart.com/',
617 'admin_notices' => apply_filters('fluent_cart/admin_notices', []),
618 // POSTs data-backfills/run until done; endpoint requires manage_options
619 'has_data_migrations' => $hasDataMigrations,
620 'subscription_intervals' => Helper::getAvailableSubscriptionIntervalOptions(),
621
622 'datei18' => DayjsFormatter::localizedStrings(),
623 'el_strings' => TransStrings::elStrings(),
624 'wp_locale' => get_locale(),
625 'is_full_name_required' => CheckoutFieldsSchema::isFullNameRequired(),
626 'business_details' => [
627 'company_name' => [
628 'enabled' => CheckoutFieldsSchema::isCompanyNameEnabled(),
629 'required' => CheckoutFieldsSchema::isCompanyNameRequired(),
630 ],
631 'vat_number' => [
632 'enabled' => CheckoutFieldsSchema::isVatNumberEnabled(),
633 'required' => CheckoutFieldsSchema::isVatNumberRequired(),
634 ],
635 'legal_registration_id' => [
636 'enabled' => CheckoutFieldsSchema::isLegalRegistrationIdEnabled(),
637 'required' => CheckoutFieldsSchema::isLegalRegistrationIdRequired(),
638 ],
639 ],
640 'fct_editor_frame' => '',
641 ]);
642
643 wp_localize_script($slug . '_admin_app_start', 'fluentCartAdminApp', $adminLocalizeData);
644
645 wp_localize_script($slug . '_admin_app_start', 'fluentCartRestVars', [
646 'rest' => $restVars,
647 ]);
648
649 do_action('fluent_cart/admin_js_loaded', $app);
650 }
651
652 public function globalEnqueueAssets($adminBar)
653 {
654 if (function_exists('get_current_screen')) {
655 $screen = get_current_screen();
656 } else {
657 $screen = null;
658 }
659
660 if ($screen && $screen->base !== 'user-edit') {
661 return;
662 }
663
664 $app = App::getInstance();
665
666 $slug = $app->config->get('app.slug');
667 if (is_admin()) {
668 wp_enqueue_script($slug . '_edit_wp_user_admin_global_js', Vite::getEnqueuePath('admin/utils/edit-wp-user-global.js'), ['jquery'], FLUENTCART_VERSION, true);
669 }
670
671
672 $baseUrl = apply_filters('fluent_cart/admin_base_url', admin_url('admin.php?page=fluent-cart#/'), []);
673
674 $editingUserVars = null;
675
676 if (function_exists('get_current_screen')) {
677 $currentScreen = get_current_screen();
678 if ($currentScreen && $currentScreen->id == 'user-edit') {
679 $userId = (int)($_GET['user_id'] ?? 0);
680 $user = get_user_by('ID', $userId);
681
682 if ($userId && $user) {
683 $fluentcartProfile = Customer::query()->where('email', $user->user_email)
684 ->orWhere('user_id', $user->ID)
685 ->first();
686 if ($fluentcartProfile) {
687 $fluentcartProfileUrl = $baseUrl . 'customers/' . $fluentcartProfile->id . '/view';
688 $editingUserVars = [
689 'user_id' => $userId,
690 'fct_profile_id' => $fluentcartProfile->id,
691 'fct_profile_url' => $fluentcartProfileUrl
692 ];
693 }
694 }
695 }
696 }
697 wp_localize_script('fluent-cart_edit_wp_user_admin_global_js', 'fluentcart_edit_user_global_bar_vars', [
698 'customer_base' => $baseUrl . 'customers/',
699 'edit_user_vars' => $editingUserVars
700 ]);
701 }
702
703 protected function getMenuIcon(): string
704 {
705 $svg = '<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100" fill="none">
706 <g clip-path="url(#clip0_394_8994)">
707 <path d="M90 0C95.5229 0 100 4.47715 100 10V90C100 95.5229 95.5229 100 90 100H10C4.47715 100 0 95.5229 0 90V10C0 4.47715 4.47715 0 10 0H90ZM28.3525 52.5986C24.8927 52.5988 21.7641 54.6544 20.3906 57.8301L15.7119 68.6484H45.5205C52.4408 68.6484 58.6996 64.5355 61.4463 58.1836L63.8613 52.5986H28.3525ZM46.6748 31.333C39.7545 31.333 33.4959 35.4459 30.749 41.7979L28.333 47.3828H70.2617C73.7217 47.3828 76.8513 45.3264 78.2246 42.1504L82.9023 31.333H46.6748Z" fill="#9CA1A8"/>
708 </g>
709 <defs>
710 <clipPath id="clip0_394_8994">
711 <rect width="100" height="100" fill="white"/>
712 </clipPath>
713 </defs>
714 </svg>';
715
716 return 'data:image/svg+xml;base64,' . base64_encode($svg);
717 }
718
719 }
720