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

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

573 lines 23.3 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\Helpers\CurrenciesHelper;
24 use FluentCart\App\Services\Filter\TaxFilter;
25 use FluentCart\App\Services\Filter\OrderFilter;
26 use FluentCart\App\Services\Filter\LicenseFilter;
27 use FluentCart\App\Services\Filter\ProductFilter;
28 use FluentCart\App\Services\Filter\CustomerFilter;
29 use FluentCart\App\Modules\Integrations\AddOnModule;
30 use FluentCart\App\Services\Translations\TransStrings;
31 use FluentCart\App\Services\Permission\PermissionManager;
32 use FluentCart\App\Modules\PaymentMethods\Core\GatewayManager;
33
34 class MenuHandler
35 {
36 public function register()
37 {
38 add_action('init', [$this, 'init']);
39
40 add_action('admin_init', function () {
41 $page = $_GET['page'] ?? '';
42
43 if ($page == 'fluent-cart') {
44
45 \FluentCart\App\Events\FirstTimePluginActivation::handle();
46 add_action('admin_enqueue_scripts', function () {
47 Vite::enqueueStyle('fluent_cart_admin_app_css',
48 'styles/tailwind/style.css',
49 );
50 Vite::enqueueScript('fluent_cart_admin_global_js',
51 'admin/global.js',
52 );
53 });
54
55 }
56 });
57
58 // Add a post display state for special WC pages.
59 add_filter('display_post_states', array($this, 'addDisplayPostStates'), 10, 2);
60
61 }
62
63
64 public function addDisplayPostStates($states, $post)
65 {
66 if (!$post || $post->post_type != 'page') {
67 return $states;
68 }
69
70
71 $targetKeys = [
72 'checkout_page_id' => __('Checkout Page', 'fluent-cart'),
73 'cart_page_id' => __('Cart Page', 'fluent-cart'),
74 'receipt_page_id' => __('Receipt Page', 'fluent-cart'),
75 'shop_page_id' => __('Shop Page', 'fluent-cart'),
76 'customer_profile_page_id' => __('Customer Profile Page', 'fluent-cart'),
77 ];
78
79 $pagesConfig = (new StoreSettings())->getPagesSettings();
80 $pagesConfig = Arr::only($pagesConfig, array_keys($targetKeys));
81
82 if (!is_array($pagesConfig)) {
83 return $states;
84 }
85 $pagesConfig = array_filter($pagesConfig);
86
87 if (empty($pagesConfig)) {
88 return $states;
89 }
90
91
92 $pagesConfig = array_flip($pagesConfig);
93 $targetKey = Arr::get($pagesConfig, $post->ID);
94
95 if ($targetKey && isset($targetKeys[$targetKey])) {
96
97 $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">
98 <rect width="300" height="300" rx="30" fill="white"/>
99 <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"/>
100 <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"/>
101 </svg>';
102
103 $states['fluent_cart'] = '<span>' . $logo . '<span>' . $targetKeys[$targetKey] . '</span></span>';
104 }
105
106 return $states;
107
108 }
109
110 public function init()
111 {
112
113 add_action('admin_menu', array($this, 'addAdminMenu'));
114
115 /*
116 * Disable Gutenberg for Products
117 */
118 add_filter('parent_file', function ($parent_file) {
119 global $submenu_file, $current_screen, $pagenow;
120
121 $postType = $_GET['post_type'] ?? '';
122 if ($postType == FluentProducts::CPT_NAME && $current_screen->post_type == FluentProducts::CPT_NAME) {
123 if ($pagenow == 'post.php') {
124 $submenu_file = 'edit.php?post_type=' . $current_screen->post_type;
125 }
126
127 if ($pagenow == 'edit-tags.php') {
128 $taxonomy = $_GET['taxonomy'] ?? '';
129 $submenu_file = 'edit-tags.php?taxonomy=' . esc_attr($taxonomy) . '&post_type=' . $current_screen->post_type;
130 }
131
132 $parent_file = 'fluent-cart';
133 }
134
135 return $parent_file;
136 });
137
138 add_action('fluent_cart/admin_menu', array($this, 'renderAdminMenu'));
139 // add_action('in_admin_header', array($this, 'maybeRenderAdminMenu'));
140
141 add_action('edit_form_top', array($this, 'pushProductNav'));
142
143 add_action('admin_bar_menu', [$this, 'globalEnqueueAssets'], 10, 1);
144 }
145
146 public function addAdminMenu()
147 {
148 $capability = 'manage_options';
149
150 if (!current_user_can('manage_options') && App::isProActive()) {
151 $capability = PermissionManager::ADMIN_CAP;
152 }
153
154 if (!current_user_can($capability)) {
155 return;
156 }
157
158 global $submenu;
159
160 $adminMenuTitle = apply_filters('fluent_cart/admin_menu_title', 'FluentCart', []);
161
162 add_menu_page(
163 $adminMenuTitle,
164 $adminMenuTitle,
165 $capability,
166 'fluent-cart',
167 [$this, 'renderAdmin'],
168 $this->getMenuIcon(),
169 apply_filters('fluent_cart/admin_menu_position', 3)
170 );
171
172 $submenu['fluent-cart']['dashboard'] = array(
173 __('Dashboard', 'fluent-cart'),
174 $capability,
175 'admin.php?page=fluent-cart#/',
176 '',
177 'fluent_cart_dashboard'
178 );
179
180 if (PermissionManager::hasPermission(['orders/view'])) {
181 $submenu['fluent-cart']['orders'] = array(
182 __('Orders', 'fluent-cart'),
183 $capability,
184 'admin.php?page=fluent-cart#/orders',
185 '',
186 'fluent_cart_orders'
187 );
188 }
189
190 if (PermissionManager::hasPermission(['customers/view', 'customers/manage'])) {
191 $submenu['fluent-cart']['customers'] = array(
192 __('Customers', 'fluent-cart'),
193 $capability,
194 'admin.php?page=fluent-cart#/customers',
195 '',
196 'fluent_cart_customers'
197 );
198 }
199
200 if (PermissionManager::hasPermission(['products/view'])) {
201 $submenu['fluent-cart']['products'] = array(
202 __('Products', 'fluent-cart'),
203 $capability,
204 'admin.php?page=fluent-cart#/products',
205 '',
206 'fluent_cart_products'
207 );
208 }
209
210 if (PermissionManager::hasPermission(['subscriptions/view'])) {
211 $submenu['fluent-cart']['subscriptions'] = array(
212 __('Subscriptions', 'fluent-cart'),
213 $capability,
214 'admin.php?page=fluent-cart#/subscriptions',
215 '',
216 'fluent_cart_subscriptions'
217 );
218 }
219
220 if (PermissionManager::hasPermission(['reports/view'])) {
221 $submenu['fluent-cart']['reports'] = array(
222 __('Reports', 'fluent-cart'),
223 $capability,
224 'admin.php?page=fluent-cart#/reports/overview',
225 '',
226 'fluent_cart_reports'
227 );
228 }
229
230 if (PermissionManager::userCan('is_super_admin')) {
231 $submenu['fluent-cart']['integrations'] = array(
232 __('Integrations', 'fluent-cart'),
233 $capability,
234 'admin.php?page=fluent-cart#/integrations',
235 '',
236 'fluent_cart_integrations'
237 );
238 }
239
240
241 $hasSensitiveAccess = PermissionManager::hasPermission(['store/settings', 'store/sensitive']);
242
243 if ($hasSensitiveAccess) {
244 $submenu['fluent-cart']['settings'] = array(
245 __('Settings', 'fluent-cart'),
246 $capability,
247 'admin.php?page=fluent-cart#/settings/store-settings/',
248 '',
249 'fluent_cart_settings'
250 );
251 }
252
253 if (PermissionManager::hasPermission(['coupons/manage', 'coupons/view'])) {
254 $submenu['fluent-cart']['coupons'] = array(
255 __('Coupons', 'fluent-cart'),
256 $capability,
257 'admin.php?page=fluent-cart#/coupons',
258 '',
259 'fluent_cart_coupons'
260 );
261 }
262
263 if ($hasSensitiveAccess && TaxModule::isTaxEnabled()) {
264 $submenu['fluent-cart']['taxes'] = array(
265 __('Taxes', 'fluent-cart'),
266 $capability,
267 'admin.php?page=fluent-cart#/taxes',
268 '',
269 'fluent_cart_taxes'
270 );
271 }
272
273
274 if (PermissionManager::hasPermission(['products/create', 'products/edit'])) {
275 $taxonomies = get_taxonomies([
276 'object_type' => [FluentProducts::CPT_NAME],
277 'public' => true,
278 ], 'object');
279
280 foreach ($taxonomies as $key => $taxonomy) {
281 $menuUrl = 'edit-tags.php?taxonomy=' . $taxonomy->name . '&post_type=' . FluentProducts::CPT_NAME;
282 $submenu['fluent-cart'][$key] = [
283 $taxonomy->label,
284 $capability,
285 $menuUrl,
286 '',
287 $taxonomy->name
288 ];
289 }
290 }
291
292 if (PermissionManager::userCan('is_super_admin')) {
293 $submenu['fluent-cart']['logs'] = array(
294 __('Logs', 'fluent-cart'),
295 $capability,
296 'admin.php?page=fluent-cart#/logs',
297 '',
298 'fluent_cart_logs'
299 );
300 }
301
302 do_action('fluent_cart/admin_submenu_added', $submenu);
303 }
304
305 public function renderAdmin()
306 {
307 $isCustomDashboardSlugChanged = (new StoreSettings())->getCustomerDashboardPageSlug() != (new StoreSettings())->get('customer_profile_page_slug');
308
309 if ($isCustomDashboardSlugChanged) {
310 $allSettings = (new StoreSettings())->get();
311 (new StoreSettings())->save($allSettings); // we are just resaving to flash the settings cache
312 }
313
314 if (function_exists('wp_enqueue_editor')) {
315 wp_enqueue_editor();
316 wp_tinymce_inline_scripts();
317 wp_enqueue_media();
318 }
319
320 $this->enqueueAssets();
321
322 $config = App::getInstance('config');
323
324 $name = $config->get('app.name');
325
326 $slug = $config->get('app.slug');
327
328 $baseUrl = apply_filters('fluent_connector_base_url', admin_url('admin.php?page=newproject#/'));
329
330 $app = App::getInstance();
331 $assets = $app['url.assets'];
332
333 App::make('view')->render('admin.admin_app', [
334 'name' => $name,
335 'slug' => $slug,
336 'baseUrl' => $baseUrl,
337 'logo' => $assets . 'images/logo.svg',
338 ]);
339 }
340
341 public function maybeRenderAdminMenu()
342 {
343 $postType = $_GET['post_type'] ?? '';
344 if ($postType == FluentProducts::CPT_NAME && is_admin()) {
345 AdminHelper::getAdminMenu(true);
346 AdminHelper::pushGlobalAdminAssets();
347 }
348 }
349
350 public function renderAdminMenu()
351 {
352 AdminHelper::getAdminMenu(true);
353 }
354
355 public function pushProductNav($post)
356 {
357 if (!$post || $post->post_type != FluentProducts::CPT_NAME) {
358 return;
359 }
360
361 AdminHelper::getProductMenu($post->ID, true, 'product_edit');
362 AdminHelper::pushGlobalAdminAssets();
363 }
364
365 public function enqueueAssets()
366 {
367 $app = App::getInstance();
368
369 $slug = $app->config->get('app.slug');
370
371 do_action('fluent_cart/loading_app', $app);
372
373 //This should be done before enqueueing the global script.
374 Vite::enqueueScript($slug . '_admin_app_start', 'admin/bootstrap/app.js', [$slug . '_global_admin_hooks']);
375
376 //Don't register this script using vite.
377 wp_enqueue_script($slug . '_global_admin_hooks', Vite::getEnqueuePath('admin/admin_hooks.js'), [], FLUENTCART_VERSION, true);
378
379 $manager = GatewayManager::getInstance();
380 $payment_routes = $manager->getRoutes();
381 // $payment_routes = apply_filters('fluent_cart/payments/payment_method_settings_routes', []);
382
383 $storage_driver_routes = apply_filters('fluent_cart/storage/storage_driver_settings_routes', [], []);
384
385 $settings = new StoreSettings();
386 $checkoutUrl = add_query_arg(Helper::INSTANT_CHECKOUT_URL_PARAM, '=', $settings->getCheckoutPage());
387 $restVars = Helper::getRestInfo();
388
389 $filterOptions = [
390 'order_filter_options' => OrderFilter::getTableFilterOptions(),
391 'customer_filter_options' => CustomerFilter::getTableFilterOptions(),
392 'product_filter_options' => ProductFilter::getTableFilterOptions(),
393 'license_filter_options' => LicenseFilter::getTableFilterOptions(),
394 'tax_filter_options' => TaxFilter::getTableFilterOptions(),
395 ];
396 $filterOptions = apply_filters('fluent_cart/admin_filter_options', $filterOptions, []);
397
398 // Structured table config keyed by table name (used by Table.js base class)
399 $tableConfig = [
400 'order_table' => ['filters' => Arr::get($filterOptions, 'order_filter_options', [])],
401 'customers' => ['filters' => Arr::get($filterOptions, 'customer_filter_options', [])],
402 'product_table' => ['filters' => Arr::get($filterOptions, 'product_filter_options', [])],
403 'licenses' => ['filters' => Arr::get($filterOptions, 'license_filter_options', [])],
404 'taxes_table' => ['filters' => Arr::get($filterOptions, 'tax_filter_options', [])],
405 'subscriptions' => ['filters' => Arr::get($filterOptions, 'subscription_filter_options', [])],
406 'shipping_zone_table' => ['filters' => Arr::get($filterOptions, 'shipping_zone_filter_options', [])],
407 ];
408
409 $tableConfig = apply_filters('fluent_cart/admin_table_saved_views', $tableConfig, [
410 'filterOptions' => $filterOptions
411 ]);
412
413 $currentUser = get_user_by('ID', get_current_user_id());
414
415 // Get app config and merge additional properties into it
416 $appConfig = $app->config->get('app');
417 $appConfig['version'] = FLUENTCART_VERSION;
418 $appConfig['permissions'] = PermissionManager::getUserPermissions();
419 $appConfig['isProActive'] = App::isProActive();
420 $appConfig['proVersion'] = defined('FLUENTCART_PRO_PLUGIN_VERSION') ? FLUENTCART_PRO_PLUGIN_VERSION : null;
421
422 $appConfig['logos'] = [
423 'dark' => Vite::getAssetUrl('images/logo/logo-full-dark.svg'),
424 'light' => Vite::getAssetUrl('images/logo/logo-full.svg'),
425 ];
426 $appConfig['isModuleTabEnabled'] = $settings->isModuleTabEnabled();
427 $appConfig['upgrade_url'] = 'https://fluentcart.com/pricing/';
428
429 $max_upload_size = wp_max_upload_size(); // Returns size in bytes
430 $adminLocalizeData = apply_filters('fluent_cart/admin_app_data', [
431 'app_config' => $appConfig,
432 'slug' => $app->config->get('app.slug'),
433 'admin_url' => admin_url('admin.php?page=fluent-cart#/'),
434 'frontend_url' => URL::getFrontEndUrl(''),
435 'nonce' => wp_create_nonce($slug),
436 'rest' => $restVars,
437 'brand_logo' => $this->getMenuIcon(),
438 'asset_url' => Vite::getAssetUrl(),
439 'me' => [
440 'id' => $currentUser->ID,
441 'full_name' => trim($currentUser->display_name),
442 'email' => $currentUser->user_email,
443 'avatar' => get_avatar_url($currentUser->ID)
444 ],
445 'shop' => Helper::shopConfig(),
446 'product_statuses' => Status::getProductStatuses(),
447 'total_products_count' => Product::query()->count(),
448 'payment_statuses' => Status::getEditableTransactionStatuses(true),
449 'payment_routes' => $payment_routes,
450 'storage_driver_routes' => $storage_driver_routes,
451 'editable_payment_statuses' => Status::getEditableTransactionStatuses(true),
452 'order_statuses' => Status::getOrderStatuses(),
453 'editable_order_statuses' => Status::getEditableOrderStatuses(),
454 'editable_customer_statuses' => Status::getEditableCustomerStatuses(),
455 // Backward compat: keep the old typo'd keys for existing frontend code
456 'order_statues' => Status::getOrderStatuses(),
457 'editable_order_statues' => Status::getEditableOrderStatuses(),
458 'editable_customer_statues' => Status::getEditableCustomerStatuses(),
459 'shipping_statuses' => Status::getShippingStatuses(),
460 'allow_bulk_payment_status_change' => true,
461 'variation_attributes' => AttributeGroup::with(['terms'])->get(),
462 'variation_terms' => AttributeTerm::query()->get()->keyBy('id'),
463 'product_image_base_uri' => Helper::getProductImageBaseUri(),
464 'add_on_modules' => AddOnModule::showAddOns(),
465 'stock_statuses' => Status::getStockStatuses(),
466 'fulfillment_types' => Helper::getFulfilmentTypes(),
467 'variation_types' => Helper::getVariationTypes(),
468 'trans' => TransStrings::getStrings(),
469 'dashboard_url' => admin_url('admin.php?page=fluent-cart#'),
470 'checkout_url' => $checkoutUrl,
471 'dummy_product_info' => apply_filters('fluent_cart/dummy_product_info', []) ?: [],
472 'currency_signs' => CurrenciesHelper::getCurrencySigns(),
473 'filter_options' => $filterOptions,
474 'table_config' => $tableConfig,
475 'receipt_page_url' => $settings->getReceiptPage(),
476 'payment_logos' => PaymentMethods::getIcons(),
477 'is_admin_bar_showing' => is_admin_bar_showing(),
478 'max_upload_size' => size_format($max_upload_size),
479 'available_shipping_method_count' => ShippingMethod::query()->where('is_enabled', '1')->count(),
480 'is_tax_enabled' => TaxModule::isTaxEnabled(),
481 'eu_vat_county_options' => TaxModule::euVatCountyOptions(),
482 'country_tax_titles' => TaxModule::taxTitleLists(),
483 'site_url' => site_url(),
484 'modules_settings' => ModuleSettings::getAllSettings(),
485 'purchase_fluent_cart_link' => 'https://fluentcart.com/',
486 'admin_notices' => apply_filters('fluent_cart/admin_notices', []),
487 'subscription_intervals' => Helper::getAvailableSubscriptionIntervalOptions(),
488
489 'datei18' => TransStrings::dateTimeStrings(),
490 'el_strings' => TransStrings::elStrings(),
491 'wp_locale' => get_locale(),
492 'is_full_name_required' => CheckoutFieldsSchema::isFullNameRequired(),
493 'fct_editor_frame' => '',
494 ]);
495
496 wp_localize_script($slug . '_admin_app_start', 'fluentCartAdminApp', $adminLocalizeData);
497
498 wp_localize_script($slug . '_admin_app_start', 'fluentCartRestVars', [
499 'rest' => $restVars,
500 ]);
501
502 do_action('fluent_cart/admin_js_loaded', $app);
503 }
504
505 public function globalEnqueueAssets($adminBar)
506 {
507 if (function_exists('get_current_screen')) {
508 $screen = get_current_screen();
509 } else {
510 $screen = null;
511 }
512
513 if ($screen && $screen->base !== 'user-edit') {
514 return;
515 }
516
517 $app = App::getInstance();
518
519 $slug = $app->config->get('app.slug');
520 if (is_admin()) {
521 wp_enqueue_script($slug . '_edit_wp_user_admin_global_js', Vite::getEnqueuePath('admin/utils/edit-wp-user-global.js'), ['jquery'], FLUENTCART_VERSION, true);
522 }
523
524
525 $baseUrl = apply_filters('fluent_cart/admin_base_url', admin_url('admin.php?page=fluent-cart#/'), []);
526
527 $editingUserVars = null;
528
529 if (function_exists('get_current_screen')) {
530 $currentScreen = get_current_screen();
531 if ($currentScreen && $currentScreen->id == 'user-edit') {
532 $userId = (int)($_GET['user_id'] ?? 0);
533 $user = get_user_by('ID', $userId);
534
535 if ($userId && $user) {
536 $fluentcartProfile = Customer::query()->where('email', $user->user_email)
537 ->orWhere('user_id', $user->ID)
538 ->first();
539 if ($fluentcartProfile) {
540 $fluentcartProfileUrl = $baseUrl . 'customers/' . $fluentcartProfile->id . '/view';
541 $editingUserVars = [
542 'user_id' => $userId,
543 'fct_profile_id' => $fluentcartProfile->id,
544 'fct_profile_url' => $fluentcartProfileUrl
545 ];
546 }
547 }
548 }
549 }
550 wp_localize_script('fluent-cart_edit_wp_user_admin_global_js', 'fluentcart_edit_user_global_bar_vars', [
551 'customer_base' => $baseUrl . 'customers/',
552 'edit_user_vars' => $editingUserVars
553 ]);
554 }
555
556 protected function getMenuIcon(): string
557 {
558 $svg = '<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100" fill="none">
559 <g clip-path="url(#clip0_394_8994)">
560 <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"/>
561 </g>
562 <defs>
563 <clipPath id="clip0_394_8994">
564 <rect width="100" height="100" fill="white"/>
565 </clipPath>
566 </defs>
567 </svg>';
568
569 return 'data:image/svg+xml;base64,' . base64_encode($svg);
570 }
571
572 }
573