PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.19
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.19
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.19, at app/Hooks/Handlers/MenuHandler.php

574 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
211 if (PermissionManager::hasPermission(['subscriptions/view'])) {
212 $submenu['fluent-cart']['subscriptions'] = array(
213 __('Subscriptions', 'fluent-cart'),
214 $capability,
215 'admin.php?page=fluent-cart#/subscriptions',
216 '',
217 'fluent_cart_subscriptions'
218 );
219 }
220
221 if (PermissionManager::hasPermission(['reports/view'])) {
222 $submenu['fluent-cart']['reports'] = array(
223 __('Reports', 'fluent-cart'),
224 $capability,
225 'admin.php?page=fluent-cart#/reports/overview',
226 '',
227 'fluent_cart_reports'
228 );
229 }
230
231 if (PermissionManager::userCan('is_super_admin')) {
232 $submenu['fluent-cart']['integrations'] = array(
233 __('Integrations', 'fluent-cart'),
234 $capability,
235 'admin.php?page=fluent-cart#/integrations',
236 '',
237 'fluent_cart_integrations'
238 );
239 }
240
241
242 $hasSensitiveAccess = PermissionManager::hasPermission(['store/settings', 'store/sensitive']);
243
244 if ($hasSensitiveAccess) {
245 $submenu['fluent-cart']['settings'] = array(
246 __('Settings', 'fluent-cart'),
247 $capability,
248 'admin.php?page=fluent-cart#/settings/store-settings/',
249 '',
250 'fluent_cart_settings'
251 );
252 }
253
254 if (PermissionManager::hasPermission(['coupons/manage', 'coupons/view'])) {
255 $submenu['fluent-cart']['coupons'] = array(
256 __('Coupons', 'fluent-cart'),
257 $capability,
258 'admin.php?page=fluent-cart#/coupons',
259 '',
260 'fluent_cart_coupons'
261 );
262 }
263
264 if ($hasSensitiveAccess && TaxModule::isTaxEnabled()) {
265 $submenu['fluent-cart']['taxes'] = array(
266 __('Taxes', 'fluent-cart'),
267 $capability,
268 'admin.php?page=fluent-cart#/taxes',
269 '',
270 'fluent_cart_taxes'
271 );
272 }
273
274
275 if (PermissionManager::hasPermission(['products/create', 'products/edit'])) {
276 $taxonomies = get_taxonomies([
277 'object_type' => [FluentProducts::CPT_NAME],
278 'public' => true,
279 ], 'object');
280
281 foreach ($taxonomies as $key => $taxonomy) {
282 $menuUrl = 'edit-tags.php?taxonomy=' . $taxonomy->name . '&post_type=' . FluentProducts::CPT_NAME;
283 $submenu['fluent-cart'][$key] = [
284 $taxonomy->label,
285 $capability,
286 $menuUrl,
287 '',
288 $taxonomy->name
289 ];
290 }
291 }
292
293 if (PermissionManager::userCan('is_super_admin')) {
294 $submenu['fluent-cart']['logs'] = array(
295 __('Logs', 'fluent-cart'),
296 $capability,
297 'admin.php?page=fluent-cart#/logs',
298 '',
299 'fluent_cart_logs'
300 );
301 }
302
303 do_action('fluent_cart/admin_submenu_added', $submenu);
304 }
305
306 public function renderAdmin()
307 {
308 $isCustomDashboardSlugChanged = (new StoreSettings())->getCustomerDashboardPageSlug() != (new StoreSettings())->get('customer_profile_page_slug');
309
310 if ($isCustomDashboardSlugChanged) {
311 $allSettings = (new StoreSettings())->get();
312 (new StoreSettings())->save($allSettings); // we are just resaving to flash the settings cache
313 }
314
315 if (function_exists('wp_enqueue_editor')) {
316 wp_enqueue_editor();
317 wp_tinymce_inline_scripts();
318 wp_enqueue_media();
319 }
320
321 $this->enqueueAssets();
322
323 $config = App::getInstance('config');
324
325 $name = $config->get('app.name');
326
327 $slug = $config->get('app.slug');
328
329 $baseUrl = apply_filters('fluent_connector_base_url', admin_url('admin.php?page=newproject#/'));
330
331 $app = App::getInstance();
332 $assets = $app['url.assets'];
333
334 App::make('view')->render('admin.admin_app', [
335 'name' => $name,
336 'slug' => $slug,
337 'baseUrl' => $baseUrl,
338 'logo' => $assets . 'images/logo.svg',
339 ]);
340 }
341
342 public function maybeRenderAdminMenu()
343 {
344 $postType = $_GET['post_type'] ?? '';
345 if ($postType == FluentProducts::CPT_NAME && is_admin()) {
346 AdminHelper::getAdminMenu(true);
347 AdminHelper::pushGlobalAdminAssets();
348 }
349 }
350
351 public function renderAdminMenu()
352 {
353 AdminHelper::getAdminMenu(true);
354 }
355
356 public function pushProductNav($post)
357 {
358 if (!$post || $post->post_type != FluentProducts::CPT_NAME) {
359 return;
360 }
361
362 AdminHelper::getProductMenu($post->ID, true, 'product_edit');
363 AdminHelper::pushGlobalAdminAssets();
364 }
365
366 public function enqueueAssets()
367 {
368 $app = App::getInstance();
369
370 $slug = $app->config->get('app.slug');
371
372 do_action('fluent_cart/loading_app', $app);
373
374 //This should be done before enqueueing the global script.
375 Vite::enqueueScript($slug . '_admin_app_start', 'admin/bootstrap/app.js', [$slug . '_global_admin_hooks']);
376
377 //Don't register this script using vite.
378 wp_enqueue_script($slug . '_global_admin_hooks', Vite::getEnqueuePath('admin/admin_hooks.js'), [], FLUENTCART_VERSION, true);
379
380 $manager = GatewayManager::getInstance();
381 $payment_routes = $manager->getRoutes();
382 // $payment_routes = apply_filters('fluent_cart/payments/payment_method_settings_routes', []);
383
384 $storage_driver_routes = apply_filters('fluent_cart/storage/storage_driver_settings_routes', [], []);
385
386 $settings = new StoreSettings();
387 $checkoutUrl = add_query_arg(Helper::INSTANT_CHECKOUT_URL_PARAM, '=', $settings->getCheckoutPage());
388 $restVars = Helper::getRestInfo();
389
390 $filterOptions = [
391 'order_filter_options' => OrderFilter::getTableFilterOptions(),
392 'customer_filter_options' => CustomerFilter::getTableFilterOptions(),
393 'product_filter_options' => ProductFilter::getTableFilterOptions(),
394 'license_filter_options' => LicenseFilter::getTableFilterOptions(),
395 'tax_filter_options' => TaxFilter::getTableFilterOptions(),
396 ];
397 $filterOptions = apply_filters('fluent_cart/admin_filter_options', $filterOptions, []);
398
399 // Structured table config keyed by table name (used by Table.js base class)
400 $tableConfig = [
401 'order_table' => ['filters' => Arr::get($filterOptions, 'order_filter_options', [])],
402 'customers' => ['filters' => Arr::get($filterOptions, 'customer_filter_options', [])],
403 'product_table' => ['filters' => Arr::get($filterOptions, 'product_filter_options', [])],
404 'licenses' => ['filters' => Arr::get($filterOptions, 'license_filter_options', [])],
405 'taxes_table' => ['filters' => Arr::get($filterOptions, 'tax_filter_options', [])],
406 'subscriptions' => ['filters' => Arr::get($filterOptions, 'subscription_filter_options', [])],
407 'shipping_zone_table' => ['filters' => Arr::get($filterOptions, 'shipping_zone_filter_options', [])],
408 ];
409
410 $tableConfig = apply_filters('fluent_cart/admin_table_saved_views', $tableConfig, [
411 'filterOptions' => $filterOptions
412 ]);
413
414 $currentUser = get_user_by('ID', get_current_user_id());
415
416 // Get app config and merge additional properties into it
417 $appConfig = $app->config->get('app');
418 $appConfig['version'] = FLUENTCART_VERSION;
419 $appConfig['permissions'] = PermissionManager::getUserPermissions();
420 $appConfig['isProActive'] = App::isProActive();
421 $appConfig['proVersion'] = defined('FLUENTCART_PRO_PLUGIN_VERSION') ? FLUENTCART_PRO_PLUGIN_VERSION : null;
422
423 $appConfig['logos'] = [
424 'dark' => Vite::getAssetUrl('images/logo/logo-full-dark.svg'),
425 'light' => Vite::getAssetUrl('images/logo/logo-full.svg'),
426 ];
427 $appConfig['isModuleTabEnabled'] = $settings->isModuleTabEnabled();
428 $appConfig['upgrade_url'] = 'https://fluentcart.com/pricing/';
429
430 $max_upload_size = wp_max_upload_size(); // Returns size in bytes
431 $adminLocalizeData = apply_filters('fluent_cart/admin_app_data', [
432 'app_config' => $appConfig,
433 'slug' => $app->config->get('app.slug'),
434 'admin_url' => admin_url('admin.php?page=fluent-cart#/'),
435 'frontend_url' => URL::getFrontEndUrl(''),
436 'nonce' => wp_create_nonce($slug),
437 'rest' => $restVars,
438 'brand_logo' => $this->getMenuIcon(),
439 'asset_url' => Vite::getAssetUrl(),
440 'me' => [
441 'id' => $currentUser->ID,
442 'full_name' => trim($currentUser->display_name),
443 'email' => $currentUser->user_email,
444 'avatar' => get_avatar_url($currentUser->ID)
445 ],
446 'shop' => Helper::shopConfig(),
447 'product_statuses' => Status::getProductStatuses(),
448 'total_products_count' => Product::query()->count(),
449 'payment_statuses' => Status::getEditableTransactionStatuses(true),
450 'payment_routes' => $payment_routes,
451 'storage_driver_routes' => $storage_driver_routes,
452 'editable_payment_statuses' => Status::getEditableTransactionStatuses(true),
453 'order_statuses' => Status::getOrderStatuses(),
454 'editable_order_statuses' => Status::getEditableOrderStatuses(),
455 'editable_customer_statuses' => Status::getEditableCustomerStatuses(),
456 // Backward compat: keep the old typo'd keys for existing frontend code
457 'order_statues' => Status::getOrderStatuses(),
458 'editable_order_statues' => Status::getEditableOrderStatuses(),
459 'editable_customer_statues' => Status::getEditableCustomerStatuses(),
460 'shipping_statuses' => Status::getShippingStatuses(),
461 'allow_bulk_payment_status_change' => true,
462 'variation_attributes' => AttributeGroup::with(['terms'])->get(),
463 'variation_terms' => AttributeTerm::query()->get()->keyBy('id'),
464 'product_image_base_uri' => Helper::getProductImageBaseUri(),
465 'add_on_modules' => AddOnModule::showAddOns(),
466 'stock_statuses' => Status::getStockStatuses(),
467 'fulfillment_types' => Helper::getFulfilmentTypes(),
468 'variation_types' => Helper::getVariationTypes(),
469 'trans' => TransStrings::getStrings(),
470 'dashboard_url' => admin_url('admin.php?page=fluent-cart#'),
471 'checkout_url' => $checkoutUrl,
472 'dummy_product_info' => apply_filters('fluent_cart/dummy_product_info', []) ?: [],
473 'currency_signs' => CurrenciesHelper::getCurrencySigns(),
474 'filter_options' => $filterOptions,
475 'table_config' => $tableConfig,
476 'receipt_page_url' => $settings->getReceiptPage(),
477 'payment_logos' => PaymentMethods::getIcons(),
478 'is_admin_bar_showing' => is_admin_bar_showing(),
479 'max_upload_size' => size_format($max_upload_size),
480 'available_shipping_method_count' => ShippingMethod::query()->where('is_enabled', '1')->count(),
481 'is_tax_enabled' => TaxModule::isTaxEnabled(),
482 'eu_vat_county_options' => TaxModule::euVatCountyOptions(),
483 'country_tax_titles' => TaxModule::taxTitleLists(),
484 'site_url' => site_url(),
485 'modules_settings' => ModuleSettings::getAllSettings(),
486 'purchase_fluent_cart_link' => 'https://fluentcart.com/',
487 'admin_notices' => apply_filters('fluent_cart/admin_notices', []),
488 'subscription_intervals' => Helper::getAvailableSubscriptionIntervalOptions(),
489
490 'datei18' => TransStrings::dateTimeStrings(),
491 'el_strings' => TransStrings::elStrings(),
492 'wp_locale' => get_locale(),
493 'is_full_name_required' => CheckoutFieldsSchema::isFullNameRequired(),
494 'fct_editor_frame' => '',
495 ]);
496
497 wp_localize_script($slug . '_admin_app_start', 'fluentCartAdminApp', $adminLocalizeData);
498
499 wp_localize_script($slug . '_admin_app_start', 'fluentCartRestVars', [
500 'rest' => $restVars,
501 ]);
502
503 do_action('fluent_cart/admin_js_loaded', $app);
504 }
505
506 public function globalEnqueueAssets($adminBar)
507 {
508 if (function_exists('get_current_screen')) {
509 $screen = get_current_screen();
510 } else {
511 $screen = null;
512 }
513
514 if ($screen && $screen->base !== 'user-edit') {
515 return;
516 }
517
518 $app = App::getInstance();
519
520 $slug = $app->config->get('app.slug');
521 if (is_admin()) {
522 wp_enqueue_script($slug . '_edit_wp_user_admin_global_js', Vite::getEnqueuePath('admin/utils/edit-wp-user-global.js'), ['jquery'], FLUENTCART_VERSION, true);
523 }
524
525
526 $baseUrl = apply_filters('fluent_cart/admin_base_url', admin_url('admin.php?page=fluent-cart#/'), []);
527
528 $editingUserVars = null;
529
530 if (function_exists('get_current_screen')) {
531 $currentScreen = get_current_screen();
532 if ($currentScreen && $currentScreen->id == 'user-edit') {
533 $userId = (int)($_GET['user_id'] ?? 0);
534 $user = get_user_by('ID', $userId);
535
536 if ($userId && $user) {
537 $fluentcartProfile = Customer::query()->where('email', $user->user_email)
538 ->orWhere('user_id', $user->ID)
539 ->first();
540 if ($fluentcartProfile) {
541 $fluentcartProfileUrl = $baseUrl . 'customers/' . $fluentcartProfile->id . '/view';
542 $editingUserVars = [
543 'user_id' => $userId,
544 'fct_profile_id' => $fluentcartProfile->id,
545 'fct_profile_url' => $fluentcartProfileUrl
546 ];
547 }
548 }
549 }
550 }
551 wp_localize_script('fluent-cart_edit_wp_user_admin_global_js', 'fluentcart_edit_user_global_bar_vars', [
552 'customer_base' => $baseUrl . 'customers/',
553 'edit_user_vars' => $editingUserVars
554 ]);
555 }
556
557 protected function getMenuIcon(): string
558 {
559 $svg = '<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100" fill="none">
560 <g clip-path="url(#clip0_394_8994)">
561 <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"/>
562 </g>
563 <defs>
564 <clipPath id="clip0_394_8994">
565 <rect width="100" height="100" fill="white"/>
566 </clipPath>
567 </defs>
568 </svg>';
569
570 return 'data:image/svg+xml;base64,' . base64_encode($svg);
571 }
572
573 }
574