$tab, 'anchor' => $anchor); } public static function parse_tab_pin($id) { $id = (string) $id; if (0 !== strpos($id, self::TAB_PIN_PREFIX)) { return null; } $rest = substr($id, strlen(self::TAB_PIN_PREFIX)); $at = strrpos($rest, '__'); if (false === $at || 0 === $at) { return null; } $tab = substr($rest, 0, $at); $key = substr($rest, $at + 2); if ('' === $tab || '' === $key) { return null; } return array('tab' => $tab, 'key' => $key); } private static $instance = null; private $modules = array(); private $subtabs = array(); private $alias_section_candidates = array(); private $alias_sections = null; public static function instance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } private function __construct() { $this->load_core(); $this->load_modules(); if ((is_admin() || (defined('WP_CLI') && WP_CLI)) && self::load_search_index()) { AIEO_Welcome_Search_Index::register(); } add_action('admin_menu', array($this, 'register_menu'), 20); add_action('admin_menu', array($this, 'reorder_admin_submenu'), 999); add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); add_action('admin_enqueue_scripts', array($this, 'enqueue_metabox_style')); add_action('admin_head', array($this, 'print_group_palette_css'), 1); add_action('wp_head', function () { if (function_exists('is_admin_bar_showing') && is_admin_bar_showing()) { $this->print_group_palette_css(); } }, 1); add_action('wp_ajax_aieo_dmm_save_recipe', array($this, 'ajax_save_recipe')); add_action('wp_ajax_aieo_dmm_save_enabled_modules', array($this, 'ajax_save_enabled_modules')); add_action('wp_ajax_aieo_dmm_delete_export_job', array($this, 'ajax_delete_export_job')); add_action('in_admin_header', array($this, 'suppress_third_party_notices'), 1); add_action('admin_print_styles', array($this, 'suppress_third_party_notices'), 1); foreach ($this->modules as $module) { $module->register_hooks(); } if (did_action('init')) { $this->register_deferred_modules(); } else { add_action('init', array($this, 'register_deferred_modules'), 0); } } public function register_deferred_modules() { $before = $this->modules; do_action('aieo_dmm_register_deferred_modules', $this); foreach (array_diff_key($this->modules, $before) as $module) { $module->register_hooks(); } usort($this->subtabs, static function ($a, $b) { return $a['priority'] <=> $b['priority']; }); } private function load_core() { $base = AIEO_DIR . 'includes/dmm/'; if ( file_exists( AIEO_DIR . 'includes/class-aieo-admin-color.php' ) ) { require_once AIEO_DIR . 'includes/class-aieo-admin-color.php'; } require_once $base . 'class-aieo-dmm-settings.php'; require_once $base . 'class-aieo-dmm-cache.php'; if (is_admin() || (defined('WP_CLI') && WP_CLI)) { $descriptions = $base . 'class-aieo-menu-descriptions.php'; if (file_exists($descriptions)) { require_once $descriptions; AIEO_Menu_Descriptions::register(); } } require_once $base . 'class-aieo-dmm-product-fp.php'; require_once $base . 'class-aieo-dmm-user-fp.php'; require_once $base . 'class-aieo-identity-bridge.php'; require_once $base . 'class-aieo-recompute-queue.php'; require_once $base . 'class-aieo-dmm-effective-prices.php'; require_once $base . 'class-aieo-dmm-effective-recos.php'; require_once $base . 'class-aieo-dmm-effective-eligibility.php'; require_once $base . 'class-aieo-dmm-badge-renderer.php'; require_once $base . 'class-aieo-module-manifest.php'; $v2_base = AIEO_DIR . 'includes/dmm/modules/_v2/class-aieo-module-v2-base.php'; if (file_exists($v2_base)) { require_once $v2_base; } $blocks_loader = AIEO_DIR . 'includes/dmm/blocks/class-aieo-blocks-loader.php'; if (file_exists($blocks_loader)) { require_once $blocks_loader; } $patterns_class = AIEO_DIR . 'includes/dmm/blocks/class-aieo-block-patterns.php'; if (file_exists($patterns_class)) { require_once $patterns_class; } $brand_page_installer = AIEO_DIR . 'includes/dmm/class-aieo-brand-page-installer.php'; if (file_exists($brand_page_installer)) { require_once $brand_page_installer; } $block_fields = AIEO_DIR . 'includes/dmm/modules/checkout-block-fields/class-aieo-checkout-block-fields.php'; if (file_exists($block_fields)) { require_once $block_fields; } $blocks_bridge = AIEO_DIR . 'includes/dmm/modules/checkout-block-fields/class-aieo-third-party-blocks-bridge.php'; if (file_exists($blocks_bridge)) { require_once $blocks_bridge; } require_once $base . 'class-aieo-dmm-pricing-engine.php'; require_once $base . 'class-aieo-dmm-pricing-rest.php'; require_once $base . 'class-aieo-dmm-main-price-filter.php'; require_once $base . 'class-aieo-dmm-role-price-filter.php'; require_once $base . 'class-aieo-dmm-sample.php'; require_once $base . 'modules/class-aieo-dmm-module-base.php'; require_once $base . 'class-aieo-dmm-ui.php'; require_once $base . 'reports/class-aieo-table.php'; require_once $base . 'reports/class-aieo-table-registry.php'; if (is_admin() || wp_doing_ajax() || (defined('WP_CLI') && WP_CLI)) { require_once $base . 'reports/class-aieo-report-registry.php'; require_once $base . 'reports/class-aieo-report-query.php'; require_once $base . 'reports/class-aieo-report-render.php'; require_once $base . 'reports/class-aieo-report-export.php'; require_once $base . 'reports/class-aieo-report-edit.php'; require_once $base . 'reports/class-aieo-report-roles.php'; require_once $base . 'reports/class-aieo-report-page.php'; if ( is_admin() ) { AIEO_Report_Roles::seed_once(); } if ( is_admin() && class_exists( 'AIEO_User_Roles' ) ) { AIEO_User_Roles::seed_once(); } require_once $base . 'reports/class-aieo-report-ajax.php'; AIEO_Report_Ajax::register(); } require_once $base . 'reports/class-aieo-public-tables.php'; AIEO_Public_Tables::register(); require_once $base . 'reports/class-aieo-report-column-settings.php'; AIEO_Report_Column_Settings::register(); foreach (array( 'class-aieo-dmm-brand-extras-data.php', 'class-aieo-dmm-need-extras-data.php', 'class-aieo-dmm-supplier-extras-data.php', 'class-aieo-dmm-cancellation-reasons-data.php', ) as $data_file) { $path = $base . $data_file; if (file_exists($path)) { require_once $path; } } require_once $base . 'services/class-aieo-dmm-offer-matcher.php'; require_once $base . 'services/class-aieo-dmm-pattern-match.php'; require_once $base . 'services/class-aieo-dmm-schedule.php'; require_once $base . 'services/class-aieo-dmm-free-shipping-rules.php'; AIEO_DMM_Free_Shipping_Rules::register(); require_once $base . 'services/class-aieo-dmm-loyalty-window.php'; require_once $base . 'services/class-aieo-dmm-schedule-boundary.php'; AIEO_DMM_Schedule_Boundary::register(); require_once $base . 'services/class-aieo-dmm-offer-attributor.php'; require_once $base . 'services/class-aieo-dmm-shipping-cost-resolver.php'; require_once $base . 'services/class-aieo-dmm-branches.php'; require_once $base . 'services/class-aieo-dmm-product-picker.php'; AIEO_DMM_Product_Picker::register_ajax(); AIEO_DMM_Offer_Attributor::instance(); AIEO_DMM_Shipping_Cost_Resolver::instance(); AIEO_DMM_Pricing_REST::instance(); AIEO_DMM_Main_Price_Filter::instance()->maybe_register(); AIEO_DMM_Role_Price_Filter::instance()->maybe_register(); add_action('save_post_product', array('AIEO_DMM_Product_FP', 'on_product_save'), 20, 1); add_action('woocommerce_update_product', array('AIEO_DMM_Product_FP', 'on_product_save'), 20, 1); add_action('profile_update', array('AIEO_DMM_User_FP', 'on_user_change'), 20, 1); add_action('set_user_role', array('AIEO_DMM_User_FP', 'on_user_change'), 20, 1); add_action('add_user_role', array('AIEO_DMM_User_FP', 'on_user_change'), 20, 1); add_action('remove_user_role', array('AIEO_DMM_User_FP', 'on_user_change'), 20, 1); if (class_exists('AIEO_Identity_Bridge')) { AIEO_Identity_Bridge::bootstrap(); } if (class_exists('AIEO_Recompute_Queue')) { AIEO_Recompute_Queue::bootstrap(); } if (class_exists('AIEO_Effective_Prices')) { AIEO_Effective_Prices::bootstrap(); } if (class_exists('AIEO_Effective_Recos')) { AIEO_Effective_Recos::bootstrap(); } if (class_exists('AIEO_Effective_Eligibility')) { AIEO_Effective_Eligibility::bootstrap(); } if (class_exists('AIEO_DMM_Badge_Renderer')) { AIEO_DMM_Badge_Renderer::bootstrap(); } if (class_exists('AIEO_Module_V2_Loader')) { AIEO_Module_V2_Loader::boot_all(); } if (class_exists('AIEO_Blocks_Loader')) { AIEO_Blocks_Loader::bootstrap(); } if (class_exists('AIEO_Block_Patterns')) { AIEO_Block_Patterns::bootstrap(); } if (class_exists('AIEO_Brand_Page_Installer')) { AIEO_Brand_Page_Installer::bootstrap(); } if (class_exists('AIEO_Checkout_Block_Fields')) { AIEO_Checkout_Block_Fields::bootstrap(); } if (class_exists('AIEO_Third_Party_Blocks_Bridge')) { AIEO_Third_Party_Blocks_Bridge::bootstrap(); } if (!class_exists('AIEO_Greek_Nomi')) { $aieo_nomi_path = AIEO_DIR . 'includes/dmm/modules/address-autocomplete/class-aieo-greek-nomi.php'; if (file_exists($aieo_nomi_path)) { require_once $aieo_nomi_path; } } if (class_exists('AIEO_Greek_Nomi')) { AIEO_Greek_Nomi::register(); } if (!class_exists('AIEO_Address_Provider_Google') && class_exists('WC_Address_Provider')) { $aieo_addr_path = AIEO_DIR . 'includes/dmm/modules/address-autocomplete/class-aieo-address-provider-google.php'; if (file_exists($aieo_addr_path)) { require_once $aieo_addr_path; } } if (class_exists('AIEO_Address_Provider_Google')) { AIEO_Address_Provider_Google::register(); } if (class_exists('AIEO_DMM_Term_Relations')) { AIEO_DMM_Term_Relations::bootstrap(); } if (class_exists('AIEO_DMM_Brand_Extras_Data')) { AIEO_DMM_Brand_Extras_Data::bootstrap(); } if (class_exists('AIEO_DMM_Need_Extras_Data')) { AIEO_DMM_Need_Extras_Data::bootstrap(); } if (class_exists('AIEO_DMM_Supplier_Extras_Data')) { AIEO_DMM_Supplier_Extras_Data::bootstrap(); } if (class_exists('AIEO_DMM_Related_Tab')) { AIEO_DMM_Related_Tab::register(); } $post_relations_metabox = AIEO_DIR . 'includes/dmm/class-aieo-dmm-post-relations-metabox.php'; if (is_admin() && file_exists($post_relations_metabox)) { require_once $post_relations_metabox; if (class_exists('AIEO_DMM_Post_Relations_Metabox')) { AIEO_DMM_Post_Relations_Metabox::register(); } } foreach (array( 'class-aieo-dmm-cancellation-reasons-data.php', 'class-aieo-dmm-order-cancellations.php', 'class-aieo-dmm-product-behavior-metrics.php', 'class-aieo-dmm-user-behavior-metrics.php', 'class-aieo-dmm-campaigns.php', 'class-aieo-dmm-reco-penalty.php', 'class-aieo-dmm-risk-gating.php', 'class-aieo-dmm-cod-country-block.php', 'modules/class-aieo-dmm-payment-gating.php', 'class-aieo-dmm-payment-country-block.php', ) as $f) { $p = $base . $f; if (file_exists($p)) { require_once $p; } } if (class_exists('AIEO_DMM_Order_Cancellations')) { AIEO_DMM_Order_Cancellations::bootstrap(); } if (class_exists('AIEO_DMM_Product_Behavior_Metrics')) { AIEO_DMM_Product_Behavior_Metrics::bootstrap(); } if (class_exists('AIEO_DMM_User_Behavior_Metrics')) { AIEO_DMM_User_Behavior_Metrics::bootstrap(); } if (class_exists('AIEO_DMM_Reco_Penalty')) { AIEO_DMM_Reco_Penalty::bootstrap(); } if (class_exists('AIEO_DMM_Risk_Gating')) { AIEO_DMM_Risk_Gating::bootstrap(); } if (class_exists('AIEO_DMM_COD_Country_Block')) { AIEO_DMM_COD_Country_Block::bootstrap(); } if (class_exists('AIEO_DMM_Payment_Gating')) { AIEO_DMM_Payment_Gating::bootstrap(); } if (class_exists('AIEO_DMM_Payment_Country_Block')) { AIEO_DMM_Payment_Country_Block::bootstrap(); } if (class_exists('WooCommerce')) { $moi_bootstrap = $base . 'marketplace-order-importer/bootstrap.php'; if (file_exists($moi_bootstrap)) { require_once $moi_bootstrap; } } } private static function is_pure_frontend_request() { if (is_admin() || wp_doing_ajax() || wp_doing_cron()) { return false; } if (defined('WP_CLI') && WP_CLI) { return false; } if (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) { return false; } if (defined('REST_REQUEST') && REST_REQUEST) { return false; } if (isset($_GET['wc-ajax'])) { return false; } $uri = isset($_SERVER['REQUEST_URI']) ? (string) wp_unslash($_SERVER['REQUEST_URI']) : ''; if ($uri !== '') { $rest_prefix = function_exists('rest_get_url_prefix') ? rest_get_url_prefix() : 'wp-json'; if (strpos($uri, '/' . $rest_prefix . '/') !== false || strpos($uri, 'rest_route=') !== false) { return false; } } return true; } private function load_modules() { $modules_dir = AIEO_DIR . 'includes/dmm/modules/'; $files = array( 'class-aieo-dmm-role-pricing.php' => 'AIEO_DMM_Role_Pricing', 'class-aieo-dmm-catalogue-mode.php' => 'AIEO_DMM_Catalogue_Mode', 'class-aieo-dmm-brand-discounts.php' => 'AIEO_DMM_Brand_Discounts', 'class-aieo-dmm-brand-gifts.php' => 'AIEO_DMM_Brand_Gifts', 'class-aieo-dmm-loyalty-classes.php' => 'AIEO_DMM_Loyalty_Classes', 'class-aieo-dmm-popups.php' => 'AIEO_DMM_Popups', 'class-aieo-dmm-display-experiments.php' => 'AIEO_DMM_Display_Experiments', 'class-aieo-dmm-free-shipping.php' => 'AIEO_DMM_Free_Shipping', 'class-aieo-dmm-free-shipping-display.php' => 'AIEO_DMM_Free_Shipping_Display', 'class-aieo-dmm-side-cart.php' => 'AIEO_DMM_Side_Cart', 'class-aieo-dmm-header-actions.php' => 'AIEO_DMM_Header_Actions', 'class-aieo-dmm-sticky-atc.php' => 'AIEO_DMM_Sticky_ATC', 'class-aieo-dmm-product-needs.php' => 'AIEO_DMM_Product_Needs', 'class-aieo-dmm-product-needs-display.php' => 'AIEO_DMM_Product_Needs_Display', 'class-aieo-dmm-ratings-display.php' => 'AIEO_DMM_Ratings_Display', 'class-aieo-dmm-web-push-display.php' => 'AIEO_DMM_Web_Push_Display', 'class-aieo-dmm-wishlist-display.php' => 'AIEO_DMM_Wishlist_Display', 'class-aieo-dmm-waitlist-display.php' => 'AIEO_DMM_Waitlist_Display', 'class-aieo-dmm-product-requests-display.php' => 'AIEO_DMM_Product_Requests_Display', 'class-aieo-dmm-getting-set-up.php' => 'AIEO_DMM_Getting_Set_Up', 'class-aieo-dmm-overlaps.php' => 'AIEO_DMM_Overlaps', 'class-aieo-dmm-complementary-display.php' => 'AIEO_DMM_Complementary_Display', 'class-aieo-dmm-branch-scanner.php' => 'AIEO_DMM_Branch_Scanner', 'class-aieo-dmm-suppliers.php' => 'AIEO_DMM_Suppliers', 'class-aieo-dmm-supplier-orders.php' => 'AIEO_DMM_Supplier_Orders', 'class-aieo-dmm-invoice-scanner.php' => 'AIEO_DMM_Invoice_Scanner', 'class-aieo-dmm-erp-connectors.php' => 'AIEO_DMM_ERP_Connectors', 'class-aieo-dmm-missing-hot-sellers.php' => 'AIEO_DMM_Missing_Hot_Sellers', 'class-aieo-dmm-physical-recovery.php' => 'AIEO_DMM_Physical_Recovery', 'class-aieo-dmm-saved-abandoned-carts.php' => 'AIEO_DMM_Saved_Abandoned_Carts', 'class-aieo-dmm-problematic-variations.php' => 'AIEO_DMM_Problematic_Variations', 'class-aieo-dmm-duplicate-simples.php' => 'AIEO_DMM_Duplicate_Simples', 'class-aieo-dmm-attribute-audit.php' => 'AIEO_DMM_Attribute_Audit', 'class-aieo-dmm-orders-export.php' => 'AIEO_DMM_Orders_Export', 'class-aieo-dmm-stock-import.php' => 'AIEO_DMM_Stock_Import', 'class-aieo-dmm-price-export.php' => 'AIEO_DMM_Price_Export', 'class-aieo-dmm-marketplace-feeds.php' => 'AIEO_DMM_Marketplace_Feeds', 'class-aieo-dmm-catalog-importer.php' => 'AIEO_DMM_Catalog_Importer', 'class-aieo-dmm-marketplace-taxonomy.php' => 'AIEO_DMM_Marketplace_Taxonomy', 'class-aieo-dmm-google-reviews.php' => 'AIEO_DMM_Google_Reviews', 'class-aieo-dmm-product-exchanger.php' => 'AIEO_DMM_Product_Exchanger', 'class-aieo-dmm-fulfillment.php' => 'AIEO_DMM_Fulfillment', 'class-aieo-dmm-delivery-stats.php' => 'AIEO_DMM_Delivery_Stats', 'class-aieo-dmm-data-prep-scheduler.php' => 'AIEO_DMM_Data_Prep_Scheduler', 'class-aieo-dmm-product-orders-analytics.php' => 'AIEO_DMM_Product_Orders_Analytics', 'class-aieo-dmm-signals.php' => 'AIEO_DMM_Signals', 'class-aieo-dmm-connections.php' => 'AIEO_DMM_Connections', 'class-aieo-dmm-subscription.php' => 'AIEO_DMM_Subscription', 'class-aieo-dmm-form-insights.php' => 'AIEO_DMM_Form_Insights', 'class-aieo-dmm-customer-intelligence.php' => 'AIEO_DMM_Customer_Intelligence', 'class-aieo-dmm-lookalike-export.php' => 'AIEO_DMM_Lookalike_Export', 'class-aieo-dmm-tools.php' => 'AIEO_DMM_Tools', 'class-aieo-dmm-my-settings.php' => 'AIEO_DMM_My_Settings', 'class-aieo-dmm-plugin-usage.php' => 'AIEO_DMM_Plugin_Usage', 'class-aieo-dmm-media-cleanup.php' => 'AIEO_DMM_Media_Cleanup', 'class-aieo-dmm-button-styles.php' => 'AIEO_DMM_Button_Styles', 'class-aieo-dmm-as-cleanup.php' => 'AIEO_DMM_As_Cleanup', 'class-aieo-dmm-remote-export.php' => 'AIEO_DMM_Remote_Export', 'class-aieo-dmm-data-tables.php' => 'AIEO_DMM_Data_Tables', 'class-aieo-dmm-controls.php' => 'AIEO_DMM_Controls', 'class-aieo-dmm-questionnaires.php' => 'AIEO_DMM_Questionnaires', 'class-aieo-dmm-questionnaire-results.php' => 'AIEO_DMM_Questionnaire_Results', 'class-aieo-dmm-shipment-cost-reports.php' => 'AIEO_DMM_Shipment_Cost_Reports', 'class-aieo-dmm-offer-effectiveness.php' => 'AIEO_DMM_Offer_Effectiveness', 'class-aieo-dmm-brand-margins.php' => 'AIEO_DMM_Brand_Margins', 'class-aieo-dmm-pnl-settings.php' => 'AIEO_DMM_PNL_Settings', 'class-aieo-dmm-promotions-report.php' => 'AIEO_DMM_Promotions_Report', 'class-aieo-dmm-contributors-page.php' => 'AIEO_DMM_Contributors_Page', 'class-aieo-dmm-landing-income.php' => 'AIEO_DMM_Landing_Income', 'class-aieo-dmm-insights-chat.php' => 'AIEO_DMM_Insights_Chat', 'class-aieo-dmm-customer-profile.php' => 'AIEO_DMM_Customer_Profile', 'class-aieo-dmm-user-roles.php' => 'AIEO_DMM_User_Roles', 'class-aieo-dmm-social.php' => 'AIEO_DMM_Social', 'class-aieo-dmm-audience-builder.php' => 'AIEO_DMM_Audience_Builder', 'class-aieo-dmm-contact-discovery.php' => 'AIEO_DMM_Contact_Discovery', 'class-aieo-dmm-competition-insights.php' => 'AIEO_DMM_Competition_Insights', 'class-aieo-dmm-workflows.php' => 'AIEO_DMM_Workflows', 'class-aieo-dmm-proposals.php' => 'AIEO_DMM_Proposals', 'class-aieo-dmm-birthday.php' => 'AIEO_DMM_Birthday', 'class-aieo-dmm-contact-optin.php' => 'AIEO_DMM_Contact_Optin', 'class-aieo-dmm-address-autocomplete.php' => 'AIEO_DMM_Address_Autocomplete', 'class-aieo-dmm-checkout-invoicing.php' => 'AIEO_DMM_Checkout_Invoicing', 'class-aieo-dmm-checkout-layout.php' => 'AIEO_DMM_Checkout_Layout', 'class-aieo-dmm-account-handling.php' => 'AIEO_DMM_Account_Handling', 'class-aieo-dmm-min-order.php' => 'AIEO_DMM_Min_Order', 'class-aieo-dmm-phone-orders.php' => 'AIEO_DMM_Phone_Orders', 'class-aieo-dmm-checkout-modal.php' => 'AIEO_DMM_Checkout_Modal', 'class-aieo-dmm-social-login.php' => 'AIEO_DMM_Social_Login', 'class-aieo-dmm-captcha.php' => 'AIEO_DMM_Captcha', 'class-aieo-dmm-login-security.php' => 'AIEO_DMM_Login_Security', 'class-aieo-dmm-security-tightening.php' => 'AIEO_DMM_Security_Tightening', 'class-aieo-dmm-withdrawal.php' => 'AIEO_DMM_Withdrawal', 'class-aieo-dmm-shipping-rates.php' => 'AIEO_DMM_Shipping_Rates', 'class-aieo-dmm-dispatch-cutoff.php' => 'AIEO_DMM_Dispatch_Cutoff', 'class-aieo-dmm-local-pickup.php' => 'AIEO_DMM_Local_Pickup', 'class-aieo-dmm-smart-shipping-label.php' => 'AIEO_DMM_Smart_Shipping_Label', 'class-aieo-dmm-checkout-product-images.php' => 'AIEO_DMM_Checkout_Product_Images', 'class-aieo-dmm-product-display.php' => 'AIEO_DMM_Product_Display', 'class-aieo-dmm-payment-logos.php' => 'AIEO_DMM_Payment_Logos', 'class-aieo-dmm-product-labels.php' => 'AIEO_DMM_Product_Labels', 'class-aieo-dmm-badges.php' => 'AIEO_DMM_Badges', 'class-aieo-dmm-add-to-cart-text.php' => 'AIEO_DMM_Add_To_Cart_Text', 'class-aieo-dmm-trust-badges.php' => 'AIEO_DMM_Trust_Badges', 'class-aieo-dmm-product-meta.php' => 'AIEO_DMM_Product_Meta', 'class-aieo-dmm-product-categories.php' => 'AIEO_DMM_Product_Categories', 'class-aieo-dmm-product-card.php' => 'AIEO_DMM_Product_Card', 'class-aieo-dmm-theme-compat.php' => 'AIEO_DMM_Theme_Compat', 'class-aieo-dmm-performance.php' => 'AIEO_DMM_Performance', 'class-aieo-dmm-product-archive-card.php' => 'AIEO_DMM_Product_Archive_Card', 'class-aieo-dmm-product-video.php' => 'AIEO_DMM_Product_Video', 'class-aieo-dmm-variations-gallery.php' => 'AIEO_DMM_Variations_Gallery', 'class-aieo-dmm-gallery-layout.php' => 'AIEO_DMM_Gallery_Layout', 'class-aieo-dmm-erp-bridge.php' => 'AIEO_DMM_ERP_Bridge', 'class-aieo-dmm-erp-database.php' => 'AIEO_DMM_ERP_Database', 'class-aieo-dmm-attribution.php' => 'AIEO_DMM_Attribution', 'class-aieo-dmm-utm-explorer.php' => 'AIEO_DMM_UTM_Explorer', 'class-aieo-dmm-db-performance.php' => 'AIEO_DMM_DB_Performance', 'class-aieo-dmm-session-tracker.php' => 'AIEO_DMM_Session_Tracker', 'class-aieo-dmm-waitlist.php' => 'AIEO_DMM_Waitlist', 'class-aieo-dmm-product-requests.php' => 'AIEO_DMM_Product_Requests', 'class-aieo-dmm-contact-form.php' => 'AIEO_DMM_Contact_Form', 'class-aieo-dmm-staff-notifications.php' => 'AIEO_DMM_Staff_Notifications', 'class-aieo-dmm-wishlist.php' => 'AIEO_DMM_Wishlist', 'class-aieo-dmm-ratings.php' => 'AIEO_DMM_Ratings', 'class-aieo-dmm-web-push.php' => 'AIEO_DMM_Web_Push', 'class-aieo-dmm-surveys.php' => 'AIEO_DMM_Surveys', 'class-aieo-dmm-ai-chat-analytics.php' => 'AIEO_DMM_AI_Chat_Analytics', 'class-aieo-dmm-events.php' => 'AIEO_DMM_Events', 'class-aieo-dmm-competitions.php' => 'AIEO_DMM_Competitions', 'class-aieo-dmm-faq.php' => 'AIEO_DMM_FAQ', 'class-aieo-dmm-store.php' => 'AIEO_DMM_Store', 'class-aieo-dmm-food-beverages.php' => 'AIEO_DMM_Food_Beverages', 'class-aieo-dmm-complementary.php' => 'AIEO_DMM_Complementary', 'class-aieo-dmm-conversions.php' => 'AIEO_DMM_Conversions', 'class-aieo-dmm-consent.php' => 'AIEO_DMM_Consent', 'class-aieo-dmm-checkout-timing.php' => 'AIEO_DMM_Checkout_Timing', 'class-aieo-dmm-session-analytics.php' => 'AIEO_DMM_Session_Analytics', 'class-aieo-dmm-swatches.php' => 'AIEO_DMM_Swatches', 'class-aieo-dmm-price-history.php' => 'AIEO_DMM_Price_History', 'class-aieo-dmm-user-vitals.php' => 'AIEO_DMM_User_Vitals', 'class-aieo-dmm-newsletter-signup.php' => 'AIEO_DMM_Newsletter_Signup', 'class-aieo-dmm-navigator.php' => 'AIEO_DMM_Navigator', 'class-aieo-dmm-category-extras.php' => 'AIEO_DMM_Category_Extras', 'class-aieo-dmm-brand-extras.php' => 'AIEO_DMM_Brand_Extras', 'class-aieo-dmm-brand-display.php' => 'AIEO_DMM_Brand_Display', 'class-aieo-dmm-custom-slots.php' => 'AIEO_DMM_Custom_Slots', 'class-aieo-dmm-custom-tabs.php' => 'AIEO_DMM_Custom_Tabs', 'class-aieo-dmm-seo.php' => 'AIEO_DMM_SEO', 'class-aieo-dmm-seo-import.php' => 'AIEO_DMM_SEO_Import', 'class-aieo-dmm-seo-health.php' => 'AIEO_DMM_SEO_Health', 'class-aieo-dmm-seo-opportunities.php' => 'AIEO_DMM_SEO_Opportunities', 'class-aieo-dmm-seo-search-queries.php' => 'AIEO_DMM_SEO_Search_Queries', 'class-aieo-dmm-seo-meta-batch.php' => 'AIEO_DMM_SEO_Meta_Batch', 'class-aieo-dmm-seo-backlinks.php' => 'AIEO_DMM_SEO_Backlinks', 'class-aieo-dmm-seo-redirects.php' => 'AIEO_DMM_SEO_Redirects', 'class-aieo-dmm-seo-schema.php' => 'AIEO_DMM_SEO_Schema', 'class-aieo-dmm-seo-social.php' => 'AIEO_DMM_SEO_Social', 'class-aieo-dmm-seo-linking.php' => 'AIEO_DMM_SEO_Linking', 'class-aieo-dmm-need-extras.php' => 'AIEO_DMM_Need_Extras', 'class-aieo-dmm-tag-extras.php' => 'AIEO_DMM_Tag_Extras', 'class-aieo-dmm-supplier-extras.php' => 'AIEO_DMM_Supplier_Extras', 'class-aieo-dmm-cancellations.php' => 'AIEO_DMM_Cancellations', 'class-aieo-dmm-campaigns-admin.php' => 'AIEO_DMM_Campaigns_Admin', 'class-aieo-dmm-conversion-goals.php' => 'AIEO_DMM_Conversion_Goals', 'class-aieo-dmm-promo-surfaces.php' => 'AIEO_DMM_Promo_Surfaces', ); $aieo_food = get_option( 'aieo_food_settings', array() ); if ( is_array( $aieo_food ) && ! empty( $aieo_food['enabled'] ) && ( ! isset( $aieo_food['features']['option_groups'] ) || ! empty( $aieo_food['features']['option_groups'] ) ) ) { $files['class-aieo-dmm-option-groups.php'] = 'AIEO_DMM_Option_Groups'; } $is_frontend_request = defined('OM_REQUEST_TYPE') ? (OM_REQUEST_TYPE === 'frontend') : self::is_pure_frontend_request(); $frontend_skip = $is_frontend_request ? array( 'AIEO_DMM_Captcha', 'AIEO_DMM_Login_Security', 'AIEO_DMM_Security_Tightening', 'AIEO_DMM_Attribution', 'AIEO_DMM_Session_Analytics', 'AIEO_DMM_User_Roles', 'AIEO_DMM_Catalogue_Mode', 'AIEO_DMM_Dispatch_Cutoff', 'AIEO_DMM_Attribute_Audit', 'AIEO_DMM_Staff_Notifications', 'AIEO_DMM_Branch_Scanner', 'AIEO_DMM_Suppliers', 'AIEO_DMM_Supplier_Orders', 'AIEO_DMM_Invoice_Scanner', 'AIEO_DMM_Data_Prep_Scheduler', 'AIEO_DMM_Product_Orders_Analytics', 'AIEO_DMM_ERP_Bridge', 'AIEO_DMM_ERP_Database', 'AIEO_DMM_Fulfillment', 'AIEO_DMM_Loyalty_Classes', 'AIEO_DMM_Marketplace_Feeds', 'AIEO_DMM_Orders_Export', 'AIEO_DMM_Phone_Orders', 'AIEO_DMM_Price_Export', 'AIEO_DMM_Product_Exchanger', 'AIEO_DMM_Ratings', 'AIEO_DMM_Shipment_Cost_Reports', 'AIEO_DMM_Shipping_Rates', 'AIEO_DMM_Social_Login', 'AIEO_DMM_Stock_Import', 'AIEO_DMM_Tools', 'AIEO_DMM_Offer_Effectiveness', 'AIEO_DMM_Brand_Margins', 'AIEO_DMM_PNL_Settings', 'AIEO_DMM_As_Cleanup', 'AIEO_DMM_Data_Tables', 'AIEO_DMM_Controls', 'AIEO_DMM_Questionnaires', 'AIEO_DMM_SEO', 'AIEO_DMM_SEO_Import', 'AIEO_DMM_SEO_Health', 'AIEO_DMM_SEO_Opportunities', 'AIEO_DMM_SEO_Search_Queries', 'AIEO_DMM_SEO_Meta_Batch', 'AIEO_DMM_SEO_Backlinks', 'AIEO_DMM_SEO_Redirects', 'AIEO_DMM_SEO_Schema', 'AIEO_DMM_SEO_Social', 'AIEO_DMM_Brand_Extras', 'AIEO_DMM_Need_Extras', 'AIEO_DMM_Tag_Extras', 'AIEO_DMM_Supplier_Extras', 'AIEO_DMM_Cancellations', 'AIEO_DMM_Complementary_Display', 'AIEO_DMM_Free_Shipping_Display', 'AIEO_DMM_Product_Needs_Display', 'AIEO_DMM_Ratings_Display', 'AIEO_DMM_Waitlist_Display', 'AIEO_DMM_Web_Push_Display', 'AIEO_DMM_Wishlist_Display', ) : array(); $frontend_skip = array_flip($frontend_skip); $router_on = $is_frontend_request && class_exists('AIEO_Module_Manifest') && AIEO_Module_Manifest::is_enabled(); foreach (array( 'class-aieo-dmm-conversions-registry.php', 'class-aieo-dmm-internal-promotions.php', 'class-aieo-dmm-contributors.php', 'class-aieo-dmm-goal-conversions.php', 'class-aieo-affiliate-portal.php', ) as $conv_file) { $conv_path = AIEO_DIR . 'includes/dmm/' . $conv_file; if (file_exists($conv_path)) { require_once $conv_path; } } if (class_exists('AIEO_DMM_Internal_Promotions')) { AIEO_DMM_Internal_Promotions::register(); } if (class_exists('AIEO_DMM_Goal_Conversions')) { AIEO_DMM_Goal_Conversions::register(); } if (class_exists('AIEO_Affiliate_Portal')) { AIEO_Affiliate_Portal::register(); } if ($is_frontend_request) { $scanner_path_base = 'scanners'; if (class_exists('AIEO_DMM_Branch_Provisioner')) { $scanner_path_base = AIEO_DMM_Branch_Provisioner::path_base(); } else { $scanner_path_base = sanitize_title((string) apply_filters('aieo_dmm_scanner_path_base', 'scanners')); } $req_path = (string) wp_parse_url((string) ($_SERVER['REQUEST_URI'] ?? ''), PHP_URL_PATH); $home_path = (string) wp_parse_url(home_url('/'), PHP_URL_PATH); if ($home_path !== '' && $home_path !== '/' && strpos($req_path, $home_path) === 0) { $req_path = substr($req_path, strlen($home_path)); } $first_segment = (string) strtok(ltrim($req_path, '/'), '/'); $is_scanner_path = ($first_segment === $scanner_path_base); $scanner_delegate = function ($atts, $content = '', $tag = '') use ($is_scanner_path) { if ('aieo_dmm_customer_scanner' !== $tag && !$is_scanner_path) { return ''; } static $scanner = null; if ($scanner === null) { if (!class_exists('AIEO_DMM_Branch_Scanner')) { $p = AIEO_DIR . 'includes/dmm/modules/class-aieo-dmm-branch-scanner.php'; if (file_exists($p)) { require_once $p; } } $scanner = class_exists('AIEO_DMM_Branch_Scanner') ? new AIEO_DMM_Branch_Scanner() : false; } if (!$scanner) { return ''; } return ('aieo_dmm_customer_scanner' === $tag) ? $scanner->shortcode_customer_scanner($atts) : $scanner->shortcode_staff_scanner($atts); }; add_shortcode('aieo_dmm_branch_scanner', $scanner_delegate); add_shortcode('aieo_dmm_customer_scanner', $scanner_delegate); if ($is_scanner_path) { add_action('template_redirect', function () { if (is_singular('page') && get_post_meta((int) get_the_ID(), '_aieo_branch_scanner_page', true) && !is_user_logged_in()) { auth_redirect(); } }); add_filter('template_include', function ($template) { if (is_singular('page') && get_post_meta((int) get_the_ID(), '_aieo_branch_scanner_page', true)) { $bare = AIEO_DIR . 'includes/dmm/templates/scanner-page.php'; if (file_exists($bare)) { return $bare; } } return $template; }, PHP_INT_MAX); add_filter('wp_robots', function ($robots) { if (is_singular('page') && get_post_meta((int) get_the_ID(), '_aieo_branch_scanner_page', true)) { unset($robots['index'], $robots['follow']); $robots['noindex'] = true; $robots['nofollow'] = true; } return $robots; }, PHP_INT_MAX); } } foreach ($files as $file => $class) { if (isset($frontend_skip[$class])) { continue; } if ($router_on && !AIEO_Module_Manifest::should_load($class)) { continue; } $path = $modules_dir . $file; if (!file_exists($path)) { continue; } require_once $path; if (class_exists($class)) { $module = new $class(); if ($module instanceof AIEO_DMM_Module_Base) { $this->modules[$module->get_id()] = $module; $module_group = method_exists($module, 'get_group') ? $this->resolve_group_alias($module->get_group()) : null; $module_section = ''; if (method_exists($module, 'get_section')) { $own = $module->get_section(); if (is_string($own) && $own !== '') { $module_section = $own; } } if ($module_section === '') { $module_section = $this->resolve_module_section($module->get_id()); } if ($module_section === '') { $raw_group = method_exists($module, 'get_group') ? (string) $module->get_group() : ''; if ($raw_group !== '' && $raw_group !== $module_group) { $this->alias_section_candidates[$module->get_id()] = array($raw_group, (string) $module_group); } } if ( in_array( $module->get_id(), self::absorbed_subtabs(), true ) ) { continue; } $this->subtabs[] = array( 'id' => $module->get_id(), 'label' => '', 'priority' => $module->get_priority(), 'group' => $module_group, 'section' => $module_section, ); } } } do_action('aieo_dmm_register_dynamic_modules', $this); usort($this->subtabs, static function ($a, $b) { return $a['priority'] <=> $b['priority']; }); } public function register_module($module) { if (!($module instanceof AIEO_DMM_Module_Base)) { return; } $id = (string) $module->get_id(); if ($id === '' || isset($this->modules[$id])) { return; } $this->modules[$id] = $module; $group = method_exists($module, 'get_group') ? $this->resolve_group_alias($module->get_group()) : null; $section = ''; if (method_exists($module, 'get_section')) { $own = $module->get_section(); if (is_string($own) && $own !== '') { $section = $own; } } if ($section === '') { $section = $this->resolve_module_section($id); } $this->subtabs[] = array( 'id' => $id, 'label' => '', 'priority' => $module->get_priority(), 'group' => $group, 'section' => $section, ); } public static function group_palette() { $out = array(); foreach ((array) self::instance()->get_group_definitions() as $g) { $gid = isset($g['id']) ? (string) $g['id'] : ''; $accent = isset($g['colour']) ? (string) $g['colour'] : ''; if ($gid === '' || !preg_match('/^#[0-9A-Fa-f]{6}$/', $accent)) { continue; } $row = array('accent' => $accent); if (!empty($g['fg'])) { $row['fg'] = (string) $g['fg']; } if (!empty($g['ink'])) { $row['ink'] = (string) $g['ink']; } $out[$gid] = $row; } return $out; } public function get_active_group_id($active_id = '') { $groups = array(); foreach ($this->get_group_definitions() as $g) { $groups[$g['id']] = true; } $filtered = isset($_GET['aieo_group']) ? sanitize_key(wp_unslash($_GET['aieo_group'])) : ''; if ('' !== $filtered && isset($groups[$filtered])) { return $filtered; } $active = ('' !== (string) $active_id) ? (string) $active_id : $this->get_active_subtab_id(); if ('' === $active) { return ''; } foreach ($this->subtabs as $tab) { if (!isset($tab['id']) || $tab['id'] !== $active) { continue; } $gid = isset($tab['group']) ? (string) $tab['group'] : ''; return ($gid !== '' && isset($groups[$gid])) ? $gid : ''; } return ''; } public function print_group_palette_css() { $css = 'body.rooster-admin-has-chrome,[data-aieo-group]{--aieo-group-accent:var(--ra-screen-sec-ink,#dcdcde);--aieo-group-ink:var(--ra-screen-sec-ink,#50575e);--aieo-group-fg:var(--ra-screen-sec-on,#fff);}'; foreach (self::group_palette() as $gid => $c) { $css .= '#adminmenu .wp-submenu a.aieo-grp-' . $gid . '{border-left:3px solid ' . $c['accent'] . ';padding-left:9px;}'; $css .= '#wpadminbar .ab-submenu li.aieo-grp-' . $gid . '>.ab-item' . '{border-left:3px solid ' . $c['accent'] . ';padding-left:9px;}'; } $css .= '#wpadminbar .ab-submenu li.aieo-grp-all>.ab-item' . '{border-left:3px solid #f0f0f1;padding-left:9px;font-weight:600;}'; $css .= '#adminmenu .wp-submenu a.aieo-grp-all{border-left:3px solid #212222;padding-left:9px;font-weight:600;}'; echo '' . "\n"; } private function current_admin_page() { return isset($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : ''; } public function get_group_definitions() { return apply_filters('aieo_dmm_group_definitions', array( array( 'id' => 'home', 'label' => __('Home', 'ai-eshop-optimizer'), 'priority' => 5, 'colour' => '#212222', 'icon' => 'house', ), array( 'id' => 'catalog_sync', 'label' => __('Operations', 'ai-eshop-optimizer'), 'priority' => 10, 'colour' => '#1e63ce', 'icon' => 'package', ), array( 'id' => 'pricing_offers', 'label' => __('Pricing and offers', 'ai-eshop-optimizer'), 'priority' => 20, 'colour' => '#b794f6', 'icon' => 'tag', ), array( 'id' => 'storefront', 'label' => __('Frontend', 'ai-eshop-optimizer'), 'priority' => 30, 'colour' => '#75beff', 'icon' => 'app-window', ), array( 'id' => 'eshop_insights', 'label' => __('Insights', 'ai-eshop-optimizer'), 'priority' => 75, 'colour' => '#ffde00', 'icon' => 'trending-up', ), array( 'id' => 'customer_profiling', 'label' => __('Profiling', 'ai-eshop-optimizer'), 'priority' => 60, 'colour' => '#ff4d4d', 'icon' => 'users', ), array( 'id' => 'promote', 'label' => __('Promote', 'ai-eshop-optimizer'), 'priority' => 55, 'colour' => '#b794f6', 'icon' => 'rocket', ), array( 'id' => 'attribution', 'label' => __('Results', 'ai-eshop-optimizer'), 'priority' => 70, 'colour' => '#8fe03a', 'icon' => 'chart-column', ), array( 'id' => 'tools', 'label' => __('Settings', 'ai-eshop-optimizer'), 'priority' => 90, 'colour' => '#a1a1aa', 'icon' => 'settings', ), )); } public function get_nav_link_items() { $items = array(); foreach (array( array('documents', __('Order Documents', 'ai-eshop-optimizer'), 46, 'sync_feeds', 'scroll-text'), array('couriers', __('Couriers', 'ai-eshop-optimizer'), 47, 'couriers', 'truck'), array('settings', __('Courier Settings', 'ai-eshop-optimizer'), 48, 'couriers', 'cog'), array('maintenance', __('Courier Daily Jobs', 'ai-eshop-optimizer'), 49, 'couriers', 'repeat'), ) as $ff) { $items[] = array( 'group' => 'catalog_sync', 'section' => $ff[3], 'id' => 'fulfillment_' . $ff[0], 'label' => $ff[1], 'url' => add_query_arg( array('page' => self::MENU_SLUG, 'dmm_tab' => 'fulfillment', 'ff_section' => $ff[0]), admin_url('admin.php') ), 'priority' => $ff[2], 'icon' => $ff[4], ); } if (class_exists('AIEO_Form_CPT')) { $items[] = array( 'group' => 'customer_profiling', 'section' => 'voice', 'id' => 'forms', 'label' => __('Forms', 'ai-eshop-optimizer'), 'url' => admin_url('edit.php?post_type=' . AIEO_Form_CPT::POST_TYPE), 'priority' => 23, 'icon' => 'clipboard-list', ); } return apply_filters('aieo_dmm_nav_link_items', $items); } public function get_group_aliases() { return apply_filters('aieo_dmm_group_aliases', array( 'visuals' => 'storefront', 'product_display' => 'storefront', 'import_export' => 'catalog_sync', 'product_management' => 'catalog_sync', 'campaigns' => 'pricing_offers', 'special_pricing_offers' => 'pricing_offers', 'checkout_handling' => 'storefront', 'custom_post_types' => 'storefront', 'seo' => 'promote', 'setup' => 'tools', 'aichat' => 'tools', )); } public static function absorbed_subtabs() { return array( 'variations_gallery', 'product_video', 'need_extras', 'supplier_extras', 'tag_extras', ); } public function get_module_aliases() { $aliases = array( 'exit_intent' => 'popups', 'variations_gallery' => 'gallery_layout', 'product_video' => 'gallery_layout', 'need_extras' => 'brand_extras', 'supplier_extras' => 'brand_extras', 'tag_extras' => 'brand_extras', 'insights_t13' => 'questionnaire_results', 'insights_survey_results' => 'questionnaire_results', ); if (class_exists('AIEO_Table_Registry')) { foreach (AIEO_Table_Registry::superseded_vendor_tables() as $tid => $definition) { $aliases['insights_t' . (int) $tid] = (string) $definition; } } return apply_filters('aieo_dmm_module_aliases', $aliases); } public function resolve_group_alias($slug) { if (!is_string($slug) || $slug === '') { return $slug; } $aliases = $this->get_group_aliases(); return isset($aliases[$slug]) ? $aliases[$slug] : $slug; } public function get_menu_schema_version() { return '2026-10-25-one-table'; } public function get_section_definitions() { $defs = array( 'home' => array( array('id' => 'overview', 'label' => __('Overview', 'ai-eshop-optimizer')), ), 'pricing_offers' => array( array('id' => 'prices', 'label' => __('Prices', 'ai-eshop-optimizer')), array('id' => 'discounts', 'label' => __('Discounts and gifts', 'ai-eshop-optimizer')), array('id' => 'promotions', 'label' => __('Promotions', 'ai-eshop-optimizer')), ), 'storefront' => array( array('id' => 'custom_post_types', 'label' => __('Built-in features', 'ai-eshop-optimizer')), array('id' => 'product_cards', 'label' => __('Product cards', 'ai-eshop-optimizer')), array('id' => 'pdp', 'label' => __('Single product page', 'ai-eshop-optimizer')), array('id' => 'discoverability', 'label' => __('Discoverability', 'ai-eshop-optimizer')), array('id' => 'checkout_handling', 'label' => __('Checkout', 'ai-eshop-optimizer')), ), 'catalog_sync' => array( array('id' => 'sync_feeds', 'label' => __('Orders', 'ai-eshop-optimizer')), array('id' => 'couriers', 'label' => _x('Shipping', 'admin menu section heading', 'ai-eshop-optimizer'), 'stacks' => true), array('id' => 'stock_management', 'label' => __('Stock and suppliers', 'ai-eshop-optimizer')), array('id' => 'marketplaces', 'label' => __('Marketplaces', 'ai-eshop-optimizer'), 'stacks' => true), array('id' => 'catalogue', 'label' => __('Catalogue', 'ai-eshop-optimizer')), array('id' => 'import', 'label' => __('Import and export', 'ai-eshop-optimizer')), ), ); $defs['attribution'] = array( array('id' => 'sources', 'label' => __('Traffic and attribution', 'ai-eshop-optimizer')), array('id' => 'costs_roi', 'label' => __('Costs and ROI', 'ai-eshop-optimizer')), array('id' => 'service', 'label' => __('Delivery and service', 'ai-eshop-optimizer')), array('id' => 'recovery', 'label' => __('Missed and recovered revenue', 'ai-eshop-optimizer')), ); $defs['customer_profiling'] = array( array('id' => 'demand', 'label' => __('Demand signals', 'ai-eshop-optimizer')), array('id' => 'records', 'label' => __('Customer records', 'ai-eshop-optimizer')), array('id' => 'reach', 'label' => __('Contacts and audiences', 'ai-eshop-optimizer')), array('id' => 'voice', 'label' => __('Voice of the customer', 'ai-eshop-optimizer')), ); $defs['promote'] = array( array('id' => 'automate', 'label' => __('Automate', 'ai-eshop-optimizer')), array('id' => 'create_publish', 'label' => __('Create and publish', 'ai-eshop-optimizer'), 'stacks' => true), array('id' => 'spend', 'label' => __('Spend and results', 'ai-eshop-optimizer'), 'stacks' => true), array('id' => 'health', 'label' => __('Content and keywords', 'ai-eshop-optimizer')), array('id' => 'linking', 'label' => __('Links', 'ai-eshop-optimizer')), array('id' => 'tools', 'label' => __('SEO import', 'ai-eshop-optimizer'), 'stacks' => true), array('id' => 'general', 'label' => __('Technical SEO', 'ai-eshop-optimizer')), ); $defs['tools'] = array( array('id' => 'general', 'label' => __('General', 'ai-eshop-optimizer')), array('id' => 'panel', 'label' => __('Licence and data', 'ai-eshop-optimizer'), 'stacks' => true), array('id' => 'security', 'label' => __('Access and security', 'ai-eshop-optimizer')), array('id' => 'tracking', 'label' => __('Tracking and consent', 'ai-eshop-optimizer'), 'stacks' => true), array('id' => 'maintenance', 'label' => __('Maintenance and logs', 'ai-eshop-optimizer')), array('id' => 'costs', 'label' => __('Costs and margins', 'ai-eshop-optimizer'), 'stacks' => true), array('id' => 'external', 'label' => __('Connections', 'ai-eshop-optimizer')), array('id' => 'aichat', 'label' => __('AI Chat', 'ai-eshop-optimizer'), 'stacks' => true), ); $defs['eshop_insights'] = array( array('id' => 'helpers', 'label' => __('Ask AI', 'ai-eshop-optimizer')), array('id' => 'sales_reports', 'label' => __('Sales', 'ai-eshop-optimizer'), 'stacks' => true), array('id' => 'stock_management', 'label' => __('Stock', 'ai-eshop-optimizer')), array('id' => 'stock_reordering', 'label' => __('Reordering', 'ai-eshop-optimizer'), 'stacks' => true), array('id' => 'comparative_turnover', 'label' => __('Purchases and ERP', 'ai-eshop-optimizer')), ); return apply_filters('aieo_dmm_section_definitions', $defs); } public function get_module_section_assignments() { return apply_filters('aieo_dmm_module_section_assignments', array( 'customer_profile' => 'records', 'user_roles' => 'records', 'customer_intelligence'=> 'records', 'user_vitals' => 'records', 'contact_discovery' => 'reach', 'audience_builder' => 'reach', 'web-push' => 'reach', 'surveys' => 'voice', 'questionnaire_results' => 'voice', 'ratings' => 'voice', 'ai_chat_analytics' => 'voice', 'cancellations' => 'voice', 'contact_form' => 'voice', 'staff_notifications' => 'voice', 'waitlist' => 'demand', 'wishlist' => 'demand', 'product_requests' => 'demand', 'product_archive_card' => 'product_cards', 'product_card' => 'product_cards', 'badges' => 'product_cards', 'add_to_cart_text' => 'product_cards', 'product_labels' => 'product_cards', 'ratings_display' => 'product_cards', 'swatches' => 'product_cards', 'trust_badges' => 'product_cards', 'button_styles' => 'product_cards', 'variations_gallery' => 'pdp', 'gallery_layout' => 'pdp', 'product_meta' => 'pdp', 'product_categories' => 'pdp', 'product_display' => 'pdp', 'sticky_atc' => 'pdp', 'custom_slots' => 'pdp', 'custom_tabs' => 'pdp', 'payment_logos' => 'pdp', 'free_shipping_display'=> 'pdp', 'brand_display' => 'pdp', 'header_actions' => 'discoverability', 'navigator' => 'discoverability', 'product_needs_display'=> 'discoverability', 'complementary_display'=> 'discoverability', 'side_cart' => 'discoverability', 'wishlist_display' => 'discoverability', 'waitlist_display' => 'discoverability', 'product_requests_display' => 'discoverability', 'category_extras' => 'discoverability', 'newsletter_signup' => 'discoverability', 'branch_scanner' => 'stock_management', 'suppliers' => 'stock_management', 'supplier_orders' => 'stock_management', 'invoice_scanner' => 'stock_management', 'orders_export' => 'import', 'product_exchanger' => 'import', 'catalog_importer' => 'import', 'stock_import' => 'import', 'price_export' => 'import', 'product_needs' => 'catalogue', 'food_beverages' => 'catalogue', 'problematic_variations' => 'catalogue', 'phone_orders' => 'sync_feeds', 'marketplace_feeds' => 'marketplaces', 'fulfillment' => 'sync_feeds', 'marketplace_order_importer' => 'marketplaces', 'google_reviews' => 'custom_post_types', 'withdrawal' => 'sync_feeds', 'shipping_rates' => 'couriers', 'tools' => 'general', 'signals' => 'general', 'theme_compat' => 'general', 'plugin_usage' => 'general', 'my_settings' => 'general', 'product_orders_analytics' => 'general', 'data_tables' => 'general', 'controls' => 'general', 'media_cleanup' => 'maintenance', 'as_cleanup' => 'maintenance', 'data_prep_scheduler' => 'maintenance', 'erp_connectors' => 'external', 'erp_bridge' => 'external', 'erp_database' => 'external', 'connections' => 'external', 'remote_export' => 'external', )); } public function resolve_module_section($module_id) { $map = $this->get_module_section_assignments(); return isset($map[$module_id]) ? (string) $map[$module_id] : ''; } public function get_subtab_tree($active_id = '') { $groups = array(); foreach ($this->get_group_definitions() as $g) { $groups[$g['id']] = array( 'kind' => 'group', 'id' => $g['id'], 'label' => $g['label'], 'priority' => $g['priority'], 'children' => array(), 'has_active' => false, ); } $top = array(); foreach ($this->hydrate_labels($this->subtabs) as $tab) { $mod = isset($this->modules[$tab['id']]) ? $this->modules[$tab['id']] : null; if ($mod && method_exists($mod, 'appears_in_nav') && !$mod->appears_in_nav()) { continue; } $gid = isset($tab['group']) ? $tab['group'] : null; if ($gid !== null && isset($groups[$gid])) { $groups[$gid]['children'][] = $tab; if ($tab['id'] === $active_id) { $groups[$gid]['has_active'] = true; } } else { $top[] = array( 'kind' => 'tab', 'id' => $tab['id'], 'label' => $tab['label'], 'priority' => $tab['priority'], ); } } foreach ($this->get_nav_link_items() as $li) { $gid = isset($li['group']) ? $li['group'] : null; if ($gid !== null && isset($groups[$gid])) { $groups[$gid]['children'][] = array( 'kind' => 'link', 'id' => $li['id'], 'label' => $li['label'], 'url' => $li['url'], 'section' => isset($li['section']) ? (string) $li['section'] : '', 'priority' => isset($li['priority']) ? (int) $li['priority'] : 100, ); } } $section_defs = $this->get_section_definitions(); foreach ($groups as $g) { usort($g['children'], static function ($a, $b) { return $a['priority'] <=> $b['priority']; }); if (!empty($section_defs[$g['id']])) { $section_buckets = array(); foreach ($section_defs[$g['id']] as $sdef) { $section_buckets[$sdef['id']] = array( 'id' => $sdef['id'], 'label' => $sdef['label'], 'stacks' => !empty($sdef['stacks']), 'children' => array(), ); } $other_bucket = array( 'id' => '_other', 'label' => '', 'children' => array(), ); foreach ($g['children'] as $ch) { $sid = isset($ch['section']) ? (string) $ch['section'] : ''; if ($sid !== '' && isset($section_buckets[$sid])) { $section_buckets[$sid]['children'][] = $ch; } else { $other_bucket['children'][] = $ch; } } $g['sections'] = array(); foreach ($section_buckets as $b) { if (!empty($b['children'])) { $g['sections'][] = $b; } } if (!empty($other_bucket['children'])) { $g['sections'][] = $other_bucket; } $multi = 0; foreach ($g['sections'] as $sec) { if (count($sec['children']) > 1) { $multi++; } } if ($multi === 0) { unset($g['sections']); } } if (count($g['children']) === 1 && (($g['children'][0]['kind'] ?? 'tab') === 'tab')) { $only = $g['children'][0]; $top[] = array( 'kind' => 'tab', 'id' => $only['id'], 'label' => $only['label'], 'priority' => $g['priority'], 'group' => $g['id'], ); continue; } if (!empty($g['children'])) { $top[] = $g; } } usort($top, static function ($a, $b) { return $a['priority'] <=> $b['priority']; }); return $top; } private static function sort_by_label(array &$rows) { if (class_exists('Collator')) { $coll = new Collator(str_replace('_', '-', determine_locale())); $coll->setStrength(Collator::PRIMARY); usort($rows, static function ($a, $b) use ($coll) { return (int) $coll->compare((string) $a['label'], (string) $b['label']); }); return; } $fold = static function ($s) { $s = function_exists('mb_strtolower') ? mb_strtolower($s, 'UTF-8') : strtolower($s); return remove_accents($s); }; usort($rows, static function ($a, $b) use ($fold) { return strcmp($fold((string) $a['label']), $fold((string) $b['label'])); }); } public function build_admin_navigator_entries(): array { $tree = $this->get_subtab_tree(); $out = array(); $tab_eshop = self::get_menu_title(); $tab_keywords = array( 'catalog_importer' => 'import importer catalogue catalog products create amazon sp-api efood e-food etsy wolt marketplace barcodes sku enrich draft ingest read catalogue from marketplace vendor', 'ai_chat_analytics' => 'ai chat analytics conversation log questions customer profiling happiness rating sentiment chat-assisted conversions origin page session tracker', 'workflows' => 'campaign campaigns workflow automation automatewoo aw trigger triggers email push web push coupon offer send message order completed post-sale purchase abandoned cart checkout recovery guest created new guest welcome win-back win back birthday back in stock restock opted in opted out newsletter unsubscribe subscribe consent mutation change order status add order note change role update meta broadcast scheduled drip cross-sell followup follow-up funnel', 'audience_builder' => 'audience segment segments cohort targeting build audience lookalike ever purchased rfm lapsed vip loyal customers persona rule rules customer intelligence cdp filter who bought product brand category tag need', 'conversion_goals' => 'conversion goal goals attribution roas revenue objective target measure outcome kpi campaign goal', ); $desc = static function ($key) { if (!class_exists('AIEO_Menu_Descriptions')) { return ''; } $hit = AIEO_Menu_Descriptions::resolve($key); return ('self' === $hit['scope']) ? (string) $hit['text'] : ''; }; $shared = static function ($id) { if (!class_exists('AIEO_Welcome_Search_Index')) { $p = AIEO_DIR . 'includes/dmm/class-aieo-welcome-search-index.php'; if (file_exists($p)) { require_once $p; } } if (!class_exists('AIEO_Welcome_Search_Index')) { return ''; } return (string) AIEO_Welcome_Search_Index::terms_for($id); }; $kw = static function ($id) use ($tab_keywords, $desc, $shared) { $curated = isset($tab_keywords[$id]) ? $tab_keywords[$id] : ''; $d = $desc($id); $s = $shared($id); return trim($curated . ($s !== '' ? ' ' . $s : '') . ($d !== '' ? ' ' . $d : '')); }; $plain = static function ($s) { return html_entity_decode(wp_strip_all_tags((string) $s), ENT_QUOTES, 'UTF-8'); }; list($group_labels, $section_labels) = $this->navigator_label_maps($plain); foreach ($tree as $node) { if (!isset($node['kind'])) { continue; } if ($node['kind'] === 'tab') { $out[] = array( 'tab' => $tab_eshop, 'page' => self::MENU_SLUG, 'label' => $plain($node['label']), 'group' => null, 'gid' => '', 'gname' => '', 'sname' => '', 'owner' => '', 'slug' => (string) $node['id'], 'keywords' => $kw((string) $node['id']), 'url' => esc_url_raw(add_query_arg( array('page' => self::MENU_SLUG, 'dmm_tab' => $node['id']), admin_url('admin.php') )), ); } elseif ($node['kind'] === 'group' && !empty($node['children'])) { $sec_labels = array(); $sec_defs = $this->get_section_definitions(); if (isset($sec_defs[$node['id']])) { foreach ($sec_defs[$node['id']] as $s) { $sec_labels[$s['id']] = $plain($s['label']); } } foreach ($node['children'] as $child) { if (isset($child['kind']) && 'link' === $child['kind']) { $child_url = isset($child['url']) ? (string) $child['url'] : ''; $same_page = $child_url !== '' && strpos($child_url, 'page=' . self::MENU_SLUG) !== false; if (!$same_page) { continue; } } $group = $plain($node['label']); $sid = isset($child['section']) ? (string) $child['section'] : ''; if ($sid !== '' && isset($sec_labels[$sid])) { $group .= ' · ' . $sec_labels[$sid]; } $gid = (string) $node['id']; $gname = $plain($node['label']); $sname = ($sid !== '' && isset($sec_labels[$sid])) ? $sec_labels[$sid] : ''; $is_link = isset($child['kind']) && 'link' === $child['kind']; $child_id = (string) $child['id']; $child_url = $is_link && !empty($child['url']) ? esc_url_raw((string) $child['url']) : esc_url_raw(add_query_arg( array('page' => self::MENU_SLUG, 'dmm_tab' => $child_id), admin_url('admin.php') )); $out[] = array( 'tab' => $tab_eshop, 'page' => self::MENU_SLUG, 'label' => $plain($child['label']), 'group' => $group, 'gid' => $gid, 'gname' => $gname, 'sname' => $sname, 'owner' => '', 'slug' => $child_id, 'keywords' => $kw($child_id), 'url' => $child_url, ); } } } $g_analytics = __('Analytics', 'ai-eshop-optimizer'); $g_chat = __('AI Chat', 'ai-eshop-optimizer'); $aieo_section_cards = array(); $aieo_sections_file = AIEO_DIR . 'includes/dmm/data/section-anchors.php'; $aieo_seen_rows = array(); if (is_readable($aieo_sections_file)) { $aieo_loaded = include $aieo_sections_file; if (is_array($aieo_loaded)) { foreach ($aieo_loaded as $aieo_sc) { if (empty($aieo_sc['label']) || empty($aieo_sc['anchor']) || empty($aieo_sc['dmm_tab'])) { continue; } $aieo_row_key = $aieo_sc['dmm_tab'] . '|' . $aieo_sc['label'] . '|' . (isset($aieo_sc['query']) ? wp_json_encode($aieo_sc['query']) : ''); if (isset($aieo_seen_rows[$aieo_row_key])) { continue; } $aieo_seen_rows[$aieo_row_key] = true; $aieo_screens = self::standalone_screens(); if (isset($aieo_screens[$aieo_sc['dmm_tab']])) { $aieo_scr = $aieo_screens[$aieo_sc['dmm_tab']]; $aieo_sc['page_slug'] = $aieo_scr['page']; $aieo_sc['group'] = $aieo_scr['label']; unset($aieo_sc['dmm_tab']); $aieo_section_cards[] = $aieo_sc; continue; } if (!isset($this->modules[$aieo_sc['dmm_tab']])) { continue; } $aieo_sc['page_slug'] = self::MENU_SLUG; $aieo_section_cards[] = $aieo_sc; } } } $cards = apply_filters('aieo_dmm_admin_navigator_cards', array_merge($aieo_section_cards, array( array('label' => __('Monitor Your Server and Define Time-Out Limits', 'ai-eshop-optimizer'), 'page_slug' => 'ai-eshop-optimizer', 'anchor' => 'server-timeouts', 'group' => $g_analytics, 'keywords' => 'timeout batch size memory server χρονικο οριο'), array('label' => __('Display Order (drag-and-drop)', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'product_display', 'anchor' => 'display-order'), array('label' => __('Product page layout & element positioning (order + under-image)', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'product_display', 'anchor' => 'display-order'), array('label' => __('Feeds (outbound)', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'product_exchanger', 'query' => array('exch_section' => 'feeds')), array('label' => __('Imports (inbound)', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'product_exchanger', 'query' => array('exch_section' => 'imports')), array('label' => __('Media export', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'product_exchanger', 'query' => array('exch_section' => 'media')), array('label' => __('Run log', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'product_exchanger', 'query' => array('exch_section' => 'runlog')), array('label' => __('Exchanger settings', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'product_exchanger', 'query' => array('exch_section' => 'settings')), array('label' => __('Shipping rules', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'shipping_rates', 'query' => array('trs_section' => 'rules')), array('label' => __('Shipping import / export', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'shipping_rates', 'query' => array('trs_section' => 'csv')), array('label' => __('Locations', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'google_reviews', 'query' => array('grv_section' => 'locations')), array('label' => __('Review History', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'google_reviews', 'query' => array('grv_section' => 'history')), array('label' => __('Reviews settings', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'google_reviews', 'query' => array('grv_section' => 'settings')), array('label' => __('Cancellations overview', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'cancellations', 'query' => array('cn_section' => 'overview')), array('label' => __('Returns ledger', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'cancellations', 'query' => array('cn_section' => 'returns')), array('label' => __('Cancelled products', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'cancellations', 'query' => array('cn_section' => 'products')), array('label' => __('Cancelling customers', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'cancellations', 'query' => array('cn_section' => 'customers')), array('label' => __('Reason vocabulary', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'cancellations', 'query' => array('cn_section' => 'vocabulary')), array('label' => __('Chat analytics overview', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'ai_chat_analytics', 'query' => array('cac_section' => 'overview')), array('label' => __('Conversations', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'ai_chat_analytics', 'query' => array('cac_section' => 'conversations')), array('label' => __('Page origins', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'ai_chat_analytics', 'query' => array('cac_section' => 'origins')), array('label' => __('Products and gaps', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'ai_chat_analytics', 'query' => array('cac_section' => 'products')), array('label' => __('Chat conversions', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'ai_chat_analytics', 'query' => array('cac_section' => 'conversions')), array('label' => __('Social Login settings', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'social_login', 'query' => array('sso_section' => 'settings')), array('label' => __('Social Login migrator', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'social_login', 'query' => array('sso_section' => 'migrator')), array('label' => __('Marketplace vendors', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'marketplace_order_importer', 'query' => array('moi_section' => 'vendors')), array('label' => __('Inbound Log', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'marketplace_order_importer', 'query' => array('moi_section' => 'log')), array('label' => __('Importer settings', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'marketplace_order_importer', 'query' => array('moi_section' => 'settings')), array('label' => __('Saved & Abandoned Carts', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'saved_abandoned_carts', 'anchor' => 'saved-abandoned-carts'), array('label' => __('Abandoned cart report (lost revenue / recovery)', 'ai-eshop-optimizer'), 'page_slug' => self::MENU_SLUG, 'dmm_tab' => 'saved_abandoned_carts', 'anchor' => 'saved-abandoned-carts'), ))); $tab_map = array( 'ai-eshop-optimizer' => __('Analytics', 'ai-eshop-optimizer'), self::MENU_SLUG => self::get_menu_title(), 'ai-eshop-chat' => __('AI Chat', 'ai-eshop-optimizer'), ); $tree_by_slug = array(); foreach ($out as $row) { if (!isset($tree_by_slug[$row['slug']])) { $tree_by_slug[$row['slug']] = $row; } } $fold = static function ($s) { return preg_replace('/\s+/u', ' ', trim(mb_strtolower(html_entity_decode(wp_strip_all_tags((string) $s), ENT_QUOTES, 'UTF-8'), 'UTF-8'))); }; $tree_labels = array(); foreach ($out as $row) { $tree_labels[$row['slug'] . '|' . $fold($row['label'])] = true; } $aieo_tab_icons = array(); foreach ($this->tab_pin_index() as $aieo_t) { if ('' !== (string) $aieo_t['icon']) { $aieo_tab_icons[$aieo_t['tab'] . '|' . http_build_query($aieo_t['query'])] = (string) $aieo_t['icon']; } } $aieo_seen_dest = array(); foreach ($cards as $aieo_c) { $aieo_seen_dest[ (!empty($aieo_c['dmm_tab']) ? $aieo_c['dmm_tab'] : '') . '|' . http_build_query(isset($aieo_c['query']) ? (array) $aieo_c['query'] : array()) ] = true; } foreach ($this->tab_pin_index() as $aieo_t) { $aieo_key = $aieo_t['tab'] . '|' . http_build_query($aieo_t['query']); if (isset($aieo_seen_dest[$aieo_key])) { continue; } $aieo_seen_dest[$aieo_key] = true; $cards[] = array( 'label' => $aieo_t['label'], 'dmm_tab' => $aieo_t['tab'], 'query' => $aieo_t['query'], 'anchor' => '', 'page_slug' => self::MENU_SLUG, ); } $aieo_module_aliases = $this->get_module_aliases(); foreach ($cards as $c) { $page_slug = isset($c['page_slug']) ? (string) $c['page_slug'] : self::MENU_SLUG; $dmm_tab = !empty($c['dmm_tab']) ? (string) $c['dmm_tab'] : ''; if (self::MENU_SLUG === $page_slug && '' !== $dmm_tab && ! isset($this->modules[$dmm_tab]) && ! isset($aieo_module_aliases[$dmm_tab])) { continue; } if ($dmm_tab !== '' && isset($tree_labels[$dmm_tab . '|' . $fold($c['label'])])) { continue; } $url = self::card_url( $page_slug, $dmm_tab, isset($c['query']) ? $c['query'] : array(), isset($c['anchor']) ? (string) $c['anchor'] : '' ); $group = isset($c['group']) ? (string) $c['group'] : ''; $gid = $gname = $sname = $owner_label = ''; if ($dmm_tab !== '' && isset($tree_by_slug[$dmm_tab])) { $owner = $tree_by_slug[$dmm_tab]; $gid = isset($owner['gid']) ? (string) $owner['gid'] : ''; $gname = isset($owner['gname']) ? (string) $owner['gname'] : ''; $sname = isset($owner['sname']) ? (string) $owner['sname'] : ''; $owner_label = (string) $owner['label']; if ($group === '') { $group = (string) $owner['group'] !== '' ? (string) $owner['group'] . ' · ' . (string) $owner['label'] : (string) $owner['label']; } } if ($gname === '' && $dmm_tab !== '' && isset($this->modules[$dmm_tab])) { $mod = $this->modules[$dmm_tab]; $mgid = method_exists($mod, 'get_group') ? $this->resolve_group_alias((string) $mod->get_group()) : ''; if ($mgid !== '' && isset($group_labels[$mgid])) { $gid = $mgid; $gname = $group_labels[$mgid]; $msid = method_exists($mod, 'get_section') ? (string) $mod->get_section() : ''; if ($msid !== '' && isset($section_labels[$mgid][$msid])) { $sname = $section_labels[$mgid][$msid]; } } if ($owner_label === '' && method_exists($mod, 'get_label')) { $owner_label = $plain($mod->get_label()); } } if ($gname === '' && isset($c['group'])) { $gname = $plain((string) $c['group']); } if ($group === '' && $dmm_tab !== '' && isset($this->modules[$dmm_tab]) && method_exists($this->modules[$dmm_tab], 'get_label')) { $group = $plain($this->modules[$dmm_tab]->get_label()); } $card_slug = $page_slug . ($dmm_tab !== '' ? '/' . $dmm_tab : '') . (!empty($c['query']) && is_array($c['query']) ? '/' . implode('/', $c['query']) : '') . (!empty($c['anchor']) ? '#' . $c['anchor'] : ''); $card_kw = isset($c['keywords']) ? (string) $c['keywords'] : ''; $card_desc = $desc($card_slug); $card_icon = isset($c['icon']) ? (string) $c['icon'] : ''; if ('' === $card_icon && $dmm_tab !== '' && empty($c['anchor']) && !empty($c['query']) && is_array($c['query'])) { $aieo_tk = $dmm_tab . '|' . http_build_query($c['query']); $card_icon = isset($aieo_tab_icons[$aieo_tk]) ? $aieo_tab_icons[$aieo_tk] : ''; } $out[] = array( 'tab' => isset($tab_map[$page_slug]) ? $tab_map[$page_slug] : $page_slug, 'page' => $page_slug, 'label' => $plain($c['label']), 'icon' => $card_icon, 'group' => $group !== '' ? $group : $page_slug, 'gid' => $gid, 'gname' => $gname, 'sname' => $sname, 'owner' => $owner_label, 'slug' => $card_slug, 'keywords' => trim($card_kw . ($card_desc !== '' ? ' ' . $card_desc : '')), 'url' => $url, ); } foreach ($this->navigator_action_entries($tab_map, $plain) as $row) { $out[] = $row; } self::sort_by_label($out); return $out; } private function navigator_label_maps(callable $plain): array { $groups = array(); foreach ($this->get_group_definitions() as $g) { if (empty($g['id'])) { continue; } $groups[(string) $g['id']] = $plain(isset($g['label']) ? $g['label'] : $g['id']); } $sections = array(); foreach ($this->get_section_definitions() as $gid => $defs) { foreach ((array) $defs as $s) { if (empty($s['id'])) { continue; } $sections[(string) $gid][(string) $s['id']] = $plain(isset($s['label']) ? $s['label'] : $s['id']); } } return array($groups, $sections); } private function navigator_action_entries(array $tab_map, callable $plain): array { if (!class_exists('AIEO_Capability_Registry')) { return array(); } $out = array(); list($gid_labels, $sec_labels_by_gid) = $this->navigator_label_maps($plain); foreach (AIEO_Capability_Registry::described_actions() as $row) { $label = isset($row['label']) ? trim((string) $row['label']) : ''; $page = isset($row['page']) ? (string) $row['page'] : ''; if ($label === '' || $page === '') { continue; } $module = isset($this->modules[$page]) ? $this->modules[$page] : null; if (!$module) { continue; } $page_label = method_exists($module, 'get_label') ? $plain($module->get_label()) : $page; $gid = method_exists($module, 'get_group') ? (string) $module->get_group() : ''; $gid = $this->resolve_group_alias($gid); if ($gid === '' || !isset($gid_labels[$gid])) { $gid = ''; } $sid = method_exists($module, 'get_section') ? (string) $module->get_section() : ''; $sname = ($gid !== '' && $sid !== '' && isset($sec_labels_by_gid[$gid][$sid])) ? $sec_labels_by_gid[$gid][$sid] : ''; $keywords = trim( (isset($row['description']) ? (string) $row['description'] : '') . ' ' . (isset($row['ajax']) ? (string) $row['ajax'] : '') . ' ' . (isset($row['keywords']) ? (string) $row['keywords'] : '') ); $out[] = array( 'tab' => isset($tab_map[self::MENU_SLUG]) ? $tab_map[self::MENU_SLUG] : self::MENU_SLUG, 'page' => self::MENU_SLUG, 'label' => $page_label . ' ▸ ' . $label . (!empty($row['context']) ? ' (' . (string) $row['context'] . ')' : ''), 'group' => $page_label, 'gid' => $gid, 'gname' => $gid !== '' ? $gid_labels[$gid] : $page_label, 'sname' => $sname, 'owner' => $page_label, 'slug' => self::MENU_SLUG . '/' . $page . '/action/' . (string) $row['id'], 'keywords' => $keywords, 'url' => method_exists($module, 'get_url') ? $module->get_url() : '', 'kind' => 'action', ); } return $out; } const MENU_TITLE_OPTION = 'aieo_dmm_menu_title'; const MENU_TITLE_DEFAULT = 'e-shop Manager'; public static function get_menu_title() { $stored = trim((string) get_option(self::MENU_TITLE_OPTION, '')); return $stored !== '' ? $stored : self::MENU_TITLE_DEFAULT; } const GROSS_MARGIN_OPTION = 'aieo_gross_profit_margin'; const GROSS_MARGIN_DEFAULT = 60; public static function get_gross_margin_pct() { $v = (float) get_option(self::GROSS_MARGIN_OPTION, self::GROSS_MARGIN_DEFAULT); if ($v < 1) { return (float) self::GROSS_MARGIN_DEFAULT; } if ($v > 99) { return 99.0; } return $v; } public static function get_gross_margin() { return self::get_gross_margin_pct() / 100; } const FREE_SHIP_THRESHOLD_OPTION = 'aieo_free_shipping_threshold'; const FREE_SHIP_THRESHOLD_DEFAULT = 35; public static function get_free_shipping_threshold() { $v = get_option(self::FREE_SHIP_THRESHOLD_OPTION, self::FREE_SHIP_THRESHOLD_DEFAULT); $v = (float) $v; return $v >= 0 ? $v : (float) self::FREE_SHIP_THRESHOLD_DEFAULT; } const OPS_COST_MONTHLY_OPTION = 'aieo_ops_cost_monthly'; const OPS_COST_MONTHLY_DEFAULT = 0; public static function get_ops_cost_monthly() { $v = (float) get_option(self::OPS_COST_MONTHLY_OPTION, self::OPS_COST_MONTHLY_DEFAULT); return $v > 0 ? $v : 0.0; } public static function get_ops_cost_daily() { return self::get_ops_cost_monthly() / 30.4375; } public static function pnl_basis_status() { $margin = get_option(self::GROSS_MARGIN_OPTION, false); $free_ship = get_option(self::FREE_SHIP_THRESHOLD_OPTION, false); $ops = get_option(self::OPS_COST_MONTHLY_OPTION, false); $status = array( 'margin' => ($margin !== false && $margin !== ''), 'free_ship' => ($free_ship !== false && $free_ship !== ''), 'ops_cost' => ($ops !== false && $ops !== ''), ); $status['all_set'] = $status['margin'] && $status['free_ship'] && $status['ops_cost']; $labels = array(); if (!$status['margin']) { $labels[] = __('gross margin', 'ai-eshop-optimizer'); } if (!$status['free_ship']) { $labels[] = __('free-shipping threshold', 'ai-eshop-optimizer'); } if (!$status['ops_cost']) { $labels[] = __('monthly overhead', 'ai-eshop-optimizer'); } $status['unset_labels'] = $labels; return $status; } public static function pnl_basis_notice() { $s = self::pnl_basis_status(); if ($s['all_set']) { return ''; } return sprintf( __('Estimated using our default %s, which nobody has set for this shop yet. Set it under Tools to make these figures yours.', 'ai-eshop-optimizer'), implode(__(', ', 'ai-eshop-optimizer'), $s['unset_labels']) ); } public static function get_ops_cost_for_period($from, $to) { $ff = strtotime((string) $from); $tt = strtotime((string) $to); if (!$ff || !$tt || $tt < $ff) { return 0.0; } $days = (int) floor(($tt - $ff) / DAY_IN_SECONDS) + 1; return self::get_ops_cost_daily() * $days; } const REACTIVATION_SCORE_MIN = 60; public function register_menu() { $title = self::get_menu_title(); add_submenu_page( 'ai-eshop-optimizer', $title, $title, AIEO_CAPABILITY, self::MENU_SLUG, array($this, 'render_page') ); } public function reorder_admin_submenu() { global $submenu; $parent = 'ai-eshop-optimizer'; if (empty($submenu[$parent]) || !is_array($submenu[$parent])) { return; } $found_idx = null; foreach ($submenu[$parent] as $idx => $item) { if (isset($item[2]) && $item[2] === self::MENU_SLUG) { $found_idx = $idx; break; } } if ($found_idx === null) { return; } $entry = $submenu[$parent][$found_idx]; unset($submenu[$parent][$found_idx]); $submenu[$parent] = array_values($submenu[$parent]); array_splice($submenu[$parent], 1, 0, array($entry)); } public function render_page() { if (!current_user_can(AIEO_CAPABILITY)) { wp_die(esc_html__('You do not have permission to access this page.', 'ai-eshop-optimizer')); } $active_id = $this->get_active_subtab_id(); $module = isset($this->modules[$active_id]) ? $this->modules[$active_id] : null; if (null === $module && !empty($this->subtabs)) { $active_id = (string) $this->subtabs[0]['id']; $module = isset($this->modules[$active_id]) ? $this->modules[$active_id] : null; } include AIEO_DIR . 'assets/pages/dmm/page.php'; } public function get_active_subtab_label() { $group = isset($_GET['aieo_group']) ? sanitize_key(wp_unslash($_GET['aieo_group'])) : ''; if ('' !== $group) { foreach ((array) $this->get_group_definitions() as $definition) { if (isset($definition['id'], $definition['label']) && $group === (string) $definition['id']) { return wp_strip_all_tags((string) $definition['label']); } } } $id = $this->get_active_subtab_id(); if ('' === (string) $id || !isset($this->modules[$id])) { return ''; } return wp_strip_all_tags((string) $this->modules[$id]->get_label()); } public function get_active_subtab_id() { $requested = isset($_GET['dmm_tab']) ? sanitize_key(wp_unslash($_GET['dmm_tab'])) : ''; if ($requested !== '') { if (in_array($requested, self::absorbed_subtabs(), true)) { $aliases = $this->get_module_aliases(); if (isset($aliases[$requested]) && isset($this->modules[$aliases[$requested]])) { return $aliases[$requested]; } } if (isset($this->modules[$requested])) { return $requested; } $aliases = $this->get_module_aliases(); if (isset($aliases[$requested]) && isset($this->modules[$aliases[$requested]])) { return $aliases[$requested]; } } $uid = get_current_user_id(); if ($uid) { $landing = (string) get_user_meta($uid, self::LANDING_USER_META, true); if ($landing === '__resume__') { if (class_exists('AIEO_DMM_Plugin_Usage')) { $last = AIEO_DMM_Plugin_Usage::last_tab_for_user($uid); if ($last !== '' && isset($this->modules[$last])) { return $last; } } } elseif ($landing !== '' && isset($this->modules[$landing])) { return $landing; } } return self::INDEX_TAB; } public static function load_search_index() { if (class_exists('AIEO_Welcome_Search_Index')) { return true; } $path = AIEO_DIR . 'includes/dmm/class-aieo-welcome-search-index.php'; if (!file_exists($path)) { return false; } require_once $path; return class_exists('AIEO_Welcome_Search_Index'); } public function get_subtab_url($subtab_id) { return add_query_arg( array('page' => self::MENU_SLUG, 'dmm_tab' => $subtab_id), admin_url('admin.php') ); } private static function card_url($page_slug, $dmm_tab, $query, $anchor) { $args = array('page' => (string) $page_slug); if ((string) $dmm_tab !== '') { $args['dmm_tab'] = (string) $dmm_tab; } if (!empty($query) && is_array($query)) { foreach ($query as $qk => $qv) { $args[sanitize_key((string) $qk)] = (string) $qv; } } $url = esc_url_raw(add_query_arg($args, admin_url('admin.php'))); if ((string) $anchor !== '') { $url .= '#aieo-card-' . rawurlencode((string) $anchor); } return $url; } public static function standalone_screens() { return array( 'ai-eshop-optimizer' => array( 'page' => 'ai-eshop-optimizer', 'label' => __('Analytics', 'ai-eshop-optimizer'), 'group' => 'insights', 'page_pin_tab' => '', 'nav_label' => __('Analytics setup', 'ai-eshop-optimizer'), 'nav_group' => 'tools', 'nav_section' => 'panel', 'icon' => 'sliders-horizontal', ), 'ai-eshop-chat' => array( 'page' => 'ai-eshop-chat', 'label' => __('AI Chat', 'ai-eshop-optimizer'), 'group' => 'insights', 'page_pin_tab' => 'insights_chat', 'nav_label' => __('AI Chat setup', 'ai-eshop-optimizer'), 'nav_group' => 'tools', 'nav_section' => 'aichat', 'icon' => 'bot', ), 'aieo-iapi' => array( 'page' => 'aieo-iapi', 'label' => __('Interactivity (IAPI)', 'ai-eshop-optimizer'), 'group' => 'tools', 'page_pin_tab' => '', 'nav_label' => __('Interactivity (IAPI)', 'ai-eshop-optimizer'), 'nav_group' => 'tools', 'nav_section' => 'general', 'icon' => 'zap', ), 'aieo-recommendations-cache' => array( 'page' => 'aieo-recommendations-cache', 'label' => __('Recommendations Cache', 'ai-eshop-optimizer'), 'group' => 'tools', 'page_pin_tab' => '', 'nav_label' => __('Recommendations Cache', 'ai-eshop-optimizer'), 'nav_group' => 'tools', 'nav_section' => 'maintenance', 'icon' => 'database', ), ); } public function page_title_anchors() { static $memo = null; if (null !== $memo) { return $memo; } $memo = array(); $file = AIEO_DIR . 'includes/dmm/data/section-anchors.php'; if (!is_readable($file)) { return $memo; } $rows = include $file; if (!is_array($rows)) { return $memo; } foreach ($rows as $row) { if (empty($row['anchor']) || empty($row['page_title'])) { continue; } $memo[(string) $row['anchor']] = true; } if (class_exists('AIEO_Report_Page')) { foreach (AIEO_Report_Page::page_anchors() as $anchor => $pinnable) { if ($pinnable) { $memo[$anchor] = true; } else { unset($memo[$anchor]); } } } return $memo; } public function card_pin_index() { static $memo = null; if (null !== $memo) { return $memo; } $memo = array(); $file = AIEO_DIR . 'includes/dmm/data/section-anchors.php'; if (!is_readable($file)) { return $memo; } $rows = include $file; if (!is_array($rows)) { return $memo; } foreach ($rows as $row) { if (empty($row['label']) || empty($row['anchor'])) { continue; } $anchor = (string) $row['anchor']; if (!preg_match('/^[a-z0-9][a-z0-9-]*$/', $anchor)) { continue; } if (isset($memo[$anchor])) { continue; } if (!empty($row['page_title'])) { continue; } if (!empty($row['section'])) { continue; } $memo[$anchor] = array( 'label' => wp_specialchars_decode((string) $row['label'], ENT_QUOTES), 'dmm_tab' => isset($row['dmm_tab']) ? (string) $row['dmm_tab'] : '', 'query' => (isset($row['query']) && is_array($row['query'])) ? $row['query'] : array(), ); } return $memo; } private function tab_context() { static $memo = null; if (null !== $memo) { return $memo; } $memo = array(); foreach ($this->get_subtab_tree('') as $entry) { if (isset($entry['kind']) && 'group' === $entry['kind']) { foreach ((array) $entry['children'] as $child) { $memo[(string) $child['id']] = array( 'group' => (string) $entry['id'], 'label' => wp_specialchars_decode((string) $child['label'], ENT_QUOTES), ); } } else { $memo[(string) $entry['id']] = array( 'group' => '', 'label' => wp_specialchars_decode((string) $entry['label'], ENT_QUOTES), ); } } return $memo; } public function tab_pin_index() { static $memo = null; if (null !== $memo) { return $memo; } $memo = array(); foreach ($this->modules as $id => $module) { if (!method_exists($module, 'get_tabs')) { continue; } try { $tabs = (array) $module->get_tabs(); } catch (Throwable $e) { continue; } if (count($tabs) < 2) { continue; } $arg = method_exists($module, 'get_tab_arg') ? (string) $module->get_tab_arg() : ''; $norm = AIEO_DMM_UI::normalise_tabs($tabs, $arg); foreach ($norm as $key => $tab) { if (false !== strpos((string) $key, '__')) { continue; } if (empty($tab['query'])) { continue; } $memo[$id . '__' . $key] = array( 'tab' => (string) $id, 'key' => (string) $key, 'label' => (string) $tab['label'], 'query' => (array) $tab['query'], 'icon' => isset($tab['icon']) ? (string) $tab['icon'] : '', ); } } return $memo; } public function resolve_pins($pins = null) { if (null === $pins) { $uid = get_current_user_id(); if (!$uid) { return array(); } $pins = get_user_meta($uid, self::PINS_META, true); } $pins = is_array($pins) ? array_values(array_filter(array_map('sanitize_key', $pins))) : array(); if (empty($pins)) { return array(); } $known = array(); $group_of = array(); $url_of = array(); foreach ($this->get_subtab_tree('') as $entry) { if (isset($entry['kind']) && 'group' === $entry['kind']) { foreach ((array) $entry['children'] as $child) { $cid = (string) $child['id']; $known[$cid] = (string) $child['label']; $group_of[$cid] = (string) $entry['id']; if (isset($child['kind']) && 'link' === $child['kind'] && !empty($child['url'])) { $url_of[$cid] = (string) $child['url']; } } } else { $known[(string) $entry['id']] = (string) $entry['label']; } } $cards = $this->card_pin_index(); $ctx = $this->tab_context(); $out = array(); foreach ($pins as $id) { if (isset($known[$id])) { $out[] = array( 'id' => $id, 'label' => wp_specialchars_decode($known[$id], ENT_QUOTES), 'url' => isset($url_of[$id]) ? $url_of[$id] : $this->get_subtab_url($id), 'group' => isset($group_of[$id]) ? $group_of[$id] : '', 'kind' => 'page', 'owner' => '', ); continue; } $parsed_tab = self::parse_tab_pin($id); if ($parsed_tab) { $key = $parsed_tab['tab'] . '__' . $parsed_tab['key']; $tabs = $this->tab_pin_index(); if (!isset($tabs[$key])) { continue; } $row = $tabs[$key]; $page = $row['tab']; if (!isset($this->modules[$page])) { continue; } $page_label = isset($known[$page]) ? $known[$page] : (isset($ctx[$page]['label']) ? $ctx[$page]['label'] : $page); $out[] = array( 'id' => $id, 'label' => wp_specialchars_decode($page_label, ENT_QUOTES) . ' — ' . wp_specialchars_decode($row['label'], ENT_QUOTES), 'url' => add_query_arg($row['query'], $this->get_subtab_url($page)), 'group' => isset($group_of[$page]) ? $group_of[$page] : (isset($ctx[$page]['group']) ? $ctx[$page]['group'] : ''), 'kind' => 'tab', 'owner' => $page, ); continue; } $parsed = self::parse_card_pin($id); if (!$parsed) { continue; } $tab = $parsed['tab']; $anchor = $parsed['anchor']; if (!isset($cards[$anchor])) { continue; } $screens = self::standalone_screens(); if (isset($screens[$tab])) { $row = $cards[$anchor]; $out[] = array( 'id' => $id, 'label' => $row['label'], 'url' => add_query_arg('page', $screens[$tab]['page'], admin_url('admin.php')) . '#aieo-card-' . $anchor, 'group' => $screens[$tab]['group'], 'kind' => 'card', 'owner' => $screens[$tab]['label'], ); continue; } if (!isset($this->modules[$tab])) { continue; } $row = $cards[$anchor]; $query = ($row['dmm_tab'] === $tab) ? $row['query'] : array(); $out[] = array( 'id' => $id, 'label' => $row['label'], 'url' => self::card_url(self::MENU_SLUG, $tab, $query, $anchor), 'group' => isset($ctx[$tab]['group']) ? $ctx[$tab]['group'] : '', 'kind' => 'card', 'owner' => isset($ctx[$tab]['label']) ? $ctx[$tab]['label'] : '', ); } return $out; } private function enqueue_persona_styles() { if (class_exists('AIEO_Wizard')) { AIEO_Wizard::enqueue_styles(); AIEO_Wizard::enqueue_modal_script(); } } public function get_subtabs() { return $this->hydrate_labels($this->subtabs); } private function hydrate_labels(array $tabs) { if (null === $this->alias_sections) { $this->alias_sections = array(); if (!empty($this->alias_section_candidates)) { $secs = $this->get_section_definitions(); foreach ($this->alias_section_candidates as $cid => $pair) { $valid = isset($secs[$pair[1]]) ? wp_list_pluck($secs[$pair[1]], 'id') : array(); if (in_array($pair[0], $valid, true)) { $this->alias_sections[$cid] = $pair[0]; } } } } foreach ($tabs as $i => $tab) { if (empty($tab['id'])) { continue; } if (isset($this->alias_sections[$tab['id']]) && empty($tab['section'])) { $tabs[$i]['section'] = $this->alias_sections[$tab['id']]; } if (!isset($this->modules[$tab['id']])) { continue; } $mod = $this->modules[$tab['id']]; if (method_exists($mod, 'get_label')) { $tabs[$i]['label'] = $mod->get_label(); } } return $tabs; } public function get_module($id) { return isset($this->modules[$id]) ? $this->modules[$id] : null; } public function get_modules() { return $this->modules; } public function enqueue_metabox_style($hook_suffix) { $screen = function_exists('get_current_screen') ? get_current_screen() : null; $orders = $screen && (in_array($screen->id, array('shop_order', 'edit-shop_order', 'edit-product'), true) || (function_exists('wc_get_page_screen_id') && wc_get_page_screen_id('shop-order') === $screen->id)); if (!$orders && !in_array($hook_suffix, array('post.php', 'post-new.php'), true)) { return; } $tokens = 'assets/css/aieo-tokens.css'; wp_enqueue_style('aieo-tokens', AIEO_URI . $tokens, array(), file_exists(AIEO_DIR . $tokens) ? (string) filemtime(AIEO_DIR . $tokens) : AIEO_VERSION); $css = 'assets/css/aieo-metaboxes.css'; wp_enqueue_style('aieo-metaboxes', AIEO_URI . $css, array('aieo-tokens'), file_exists(AIEO_DIR . $css) ? (string) filemtime(AIEO_DIR . $css) : AIEO_VERSION); } public function enqueue_admin_assets($hook_suffix) { $is_dmm_page = isset($_GET['page']) && sanitize_key(wp_unslash($_GET['page'])) === self::MENU_SLUG; $cur_page = isset($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : ''; $is_aieo_admin = $cur_page !== '' && strpos($cur_page, 'ai-eshop-') === 0; if ($is_aieo_admin && current_user_can('manage_options')) { if (class_exists('AIEO_Menu_Descriptions')) { AIEO_Menu_Descriptions::enqueue(); } $actions_js = AIEO_DIR . 'assets/js/aieo-admin-actions.js'; if (file_exists($actions_js) && class_exists('AIEO_Action_Telemetry')) { wp_enqueue_script( 'aieo-admin-actions', AIEO_URI . 'assets/js/aieo-admin-actions.js', array(), (string) filemtime($actions_js), true ); $dmm_tab = isset($_GET['dmm_tab']) ? sanitize_key(wp_unslash($_GET['dmm_tab'])) : ''; wp_localize_script( 'aieo-admin-actions', 'aieoAdminActions', AIEO_Action_Telemetry::script_config($dmm_tab) ); } } $is_analytics_page = ($cur_page === 'ai-eshop-optimizer'); $is_chat_page = ($cur_page === 'ai-eshop-chat'); if ($is_analytics_page || $is_chat_page) { $dmm_css_path = AIEO_DIR . 'assets/css/dmm.css'; $dmm_css_ver = file_exists($dmm_css_path) ? (string) filemtime($dmm_css_path) : AIEO_VERSION; wp_enqueue_style( self::ASSETS_HANDLE, AIEO_URI . 'assets/css/dmm.css', array(), $dmm_css_ver ); } if ($is_analytics_page || $is_chat_page) { $this->enqueue_persona_styles(); } if (!$is_dmm_page) { return; } $aieo_panel_css = AIEO_DIR . 'assets/css/aieo-admin-panel.css'; wp_enqueue_style( 'ai-eshop-optimizer-admin', AIEO_URI . 'assets/css/aieo-admin-panel.css', array(), file_exists($aieo_panel_css) ? (string) filemtime($aieo_panel_css) : AIEO_VERSION ); $dmm_css_path = AIEO_DIR . 'assets/css/dmm.css'; $dmm_css_ver = file_exists($dmm_css_path) ? (string) filemtime($dmm_css_path) : AIEO_VERSION; wp_enqueue_style( self::ASSETS_HANDLE, AIEO_URI . 'assets/css/dmm.css', array('ai-eshop-optimizer-admin'), $dmm_css_ver ); $tok_css_path = AIEO_DIR . 'assets/css/aieo-tokens.css'; wp_enqueue_style( 'aieo-tokens', AIEO_URI . 'assets/css/aieo-tokens.css', array(), file_exists($tok_css_path) ? (string) filemtime($tok_css_path) : AIEO_VERSION ); $ctl_css_path = AIEO_DIR . 'assets/css/aieo-controls.css'; wp_enqueue_style( 'aieo-controls', AIEO_URI . 'assets/css/aieo-controls.css', array('aieo-tokens'), file_exists($ctl_css_path) ? (string) filemtime($ctl_css_path) : AIEO_VERSION ); $this->enqueue_persona_styles(); $tbl_css_path = AIEO_DIR . 'assets/css/aieo-table.css'; wp_enqueue_style( 'aieo-table', AIEO_URI . 'assets/css/aieo-table.css', array(self::ASSETS_HANDLE, 'aieo-tokens', 'aieo-controls'), file_exists($tbl_css_path) ? (string) filemtime($tbl_css_path) : AIEO_VERSION ); $ctl_js_path = AIEO_DIR . 'assets/js/aieo-controls.js'; wp_enqueue_script( 'aieo-controls', AIEO_URI . 'assets/js/aieo-controls.js', array(), file_exists($ctl_js_path) ? (string) filemtime($ctl_js_path) : AIEO_VERSION, true ); $tbl_js_path = AIEO_DIR . 'assets/js/aieo-table.js'; wp_enqueue_script( 'aieo-table', AIEO_URI . 'assets/js/aieo-table.js', array(), file_exists($tbl_js_path) ? (string) filemtime($tbl_js_path) : AIEO_VERSION, true ); wp_localize_script('aieo-table', 'aieoTable', array( 'ajaxUrl' => admin_url('admin-ajax.php'), 'i18n' => array( 'error' => __('Failed to load rows.', 'ai-eshop-optimizer'), 'loading' => __('Loading…', 'ai-eshop-optimizer'), 'selected' => __('%s selected', 'ai-eshop-optimizer'), 'bulkConfirm' => __('Apply to %s rows?', 'ai-eshop-optimizer'), 'bulkNeedValue' => __('Pick a value first.', 'ai-eshop-optimizer'), 'bulkProgress' => __('%1$s done, %2$s to go…', 'ai-eshop-optimizer'), 'bulkUpdated' => __('%s updated.', 'ai-eshop-optimizer'), 'bulkUpdatedSkipped' => __('%1$d updated, %2$d skipped.', 'ai-eshop-optimizer'), ), )); $s2_script_handles = array('select2', 'selectWoo'); foreach ($s2_script_handles as $h) { if (wp_script_is($h, 'registered')) { wp_enqueue_script($h); break; } } $s2_style_handles = array('select2', 'selectWoo'); $s2_style_found = false; foreach ($s2_style_handles as $h) { if (wp_style_is($h, 'registered')) { wp_enqueue_style($h); $s2_style_found = true; break; } } if (!$s2_style_found && function_exists('WC') && defined('WC_VERSION')) { wp_enqueue_style('select2', WC()->plugin_url() . '/assets/css/select2.css', array(), WC_VERSION); } wp_enqueue_script('jquery-ui-sortable'); $dmm_js_path = AIEO_DIR . 'assets/js/dmm.js'; $dmm_js_ver = file_exists($dmm_js_path) ? (string) filemtime($dmm_js_path) : AIEO_VERSION; wp_enqueue_script( self::ASSETS_HANDLE, AIEO_URI . 'assets/js/dmm.js', array('jquery', 'jquery-ui-sortable'), $dmm_js_ver, true ); if (class_exists('AIEO_DMM_Product_Picker')) { AIEO_DMM_Product_Picker::enqueue_assets(); } wp_localize_script(self::ASSETS_HANDLE, 'aieoDmm', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce(self::NONCE_ACTION), 'activeTab' => $this->get_active_subtab_id(), 'i18n' => array( 'confirmDelete' => __('Are you sure you want to delete this item?', 'ai-eshop-optimizer'), 'saving' => __('Saving…', 'ai-eshop-optimizer'), 'saved' => __('Saved', 'ai-eshop-optimizer'), 'error' => __('An error occurred', 'ai-eshop-optimizer'), 'processing' => __('Processing…', 'ai-eshop-optimizer'), 'uploadRequired' => __('Please choose a file to upload.', 'ai-eshop-optimizer'), 'done' => __('Done', 'ai-eshop-optimizer'), 'pickBrand' => __('Pick a brand…', 'ai-eshop-optimizer'), 'pickBrands' => __('Pick brand(s)…', 'ai-eshop-optimizer'), 'pickNeed' => __('Pick a need…', 'ai-eshop-optimizer'), 'pickNeeds' => __('Pick needs…', 'ai-eshop-optimizer'), 'pickCategory' => __('Pick a category…', 'ai-eshop-optimizer'), 'pickCategories' => __('Pick categories…', 'ai-eshop-optimizer'), 'searchByNameSku' => __('Search by name or SKU…', 'ai-eshop-optimizer'), 'searchUsers' => __('Search users by email / login / name…', 'ai-eshop-optimizer'), 'picker' => array( 'deleted' => __('deleted', 'ai-eshop-optimizer'), 'draft' => __('draft', 'ai-eshop-optimizer'), 'variations' => __('variations', 'ai-eshop-optimizer'), 'searching' => __('Searching…', 'ai-eshop-optimizer'), 'none' => __('No products found.', 'ai-eshop-optimizer'), 'failed' => __('Search failed.', 'ai-eshop-optimizer'), 'loadingV' => __('Loading variations…', 'ai-eshop-optimizer'), 'noV' => __('No variations.', 'ai-eshop-optimizer'), 'failV' => __('Failed to load variations.', 'ai-eshop-optimizer'), 'cancel' => __('Cancel', 'ai-eshop-optimizer'), 'done' => __('Done', 'ai-eshop-optimizer'), 'ph' => __('Search products by name or SKU…', 'ai-eshop-optimizer'), 'selOne' => __('item selected', 'ai-eshop-optimizer'), 'selMany' => __('items selected', 'ai-eshop-optimizer'), 'titleInc' => __('Choose products to INCLUDE', 'ai-eshop-optimizer'), 'titleExc' => __('Choose products to EXCLUDE', 'ai-eshop-optimizer'), 'remove' => __('Remove', 'ai-eshop-optimizer'), 'close' => __('Close', 'ai-eshop-optimizer'), 'showV' => __('Show the variations', 'ai-eshop-optimizer'), ), ), )); $module = $this->get_module($this->get_active_subtab_id()); if ($module) { $module->enqueue_admin_assets(); } } public function suppress_third_party_notices() { if (!self::is_rooster_page()) { return; } self::strip_foreign_notices(); } private static function strip_foreign_notices() { global $wp_filter; $streams = array('admin_notices', 'all_admin_notices', 'network_admin_notices', 'user_admin_notices'); foreach ($streams as $hook) { if (empty($wp_filter[$hook]) || !isset($wp_filter[$hook]->callbacks)) { continue; } foreach ($wp_filter[$hook]->callbacks as $priority => $callbacks) { foreach ($callbacks as $entry) { if (!isset($entry['function'])) { continue; } if (self::notice_is_ours($entry['function'])) { continue; } remove_action($hook, $entry['function'], $priority); } } } } private static function notice_is_ours($callback) { $name = ''; if (is_string($callback)) { $name = $callback; } elseif (is_array($callback) && isset($callback[0])) { $name = is_object($callback[0]) ? get_class($callback[0]) : (string) $callback[0]; } elseif ($callback instanceof Closure) { return false; } if ('' === $name) { return false; } if (0 === strpos(ltrim($name, '\\'), 'Rooster\\Admin\\')) { return true; } foreach (array('AIEO', 'AI_eShop', 'aieo_') as $prefix) { if (0 === stripos($name, $prefix)) { return true; } } return false; } public static function is_rooster_page() { if (!is_admin()) { return false; } $page = isset($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : ''; if ('' === $page) { return false; } return (0 === strpos($page, 'ai-eshop')) || (0 === strpos($page, 'aieo-')); } public function ajax_save_recipe() { self::verify_ajax_request(); $patch = array(); if (isset($_POST['recipe']) && is_array($_POST['recipe'])) { $patch['recipe'] = map_deep(wp_unslash($_POST['recipe']), 'sanitize_text_field'); } if (isset($_POST['bundle_mode'])) { $mode = sanitize_key(wp_unslash($_POST['bundle_mode'])); $patch['bundle_mode'] = in_array($mode, array('full', 'rec_only'), true) ? $mode : 'full'; } if (isset($_POST['pricing_mode'])) { $pmode = sanitize_key(wp_unslash($_POST['pricing_mode'])); $patch['pricing_mode'] = in_array($pmode, array('reactive', 'precomputed'), true) ? $pmode : 'reactive'; } $patch['apply_recipe_to_main_price'] = !empty($_POST['apply_recipe_to_main_price']); if (!empty($patch)) { AIEO_DMM_Settings::update($patch); AIEO_DMM_Cache::bump_version('*'); } if (class_exists('AIEO_Recommendations_Cache')) { if (method_exists('AIEO_Recommendations_Cache', 'flush_all_cache')) { AIEO_Recommendations_Cache::flush_all_cache(); } elseif (function_exists('wp_cache_flush_group')) { wp_cache_flush_group('aieo_recommendations'); } } wp_send_json_success(array('message' => __('Recipe saved.', 'ai-eshop-optimizer'))); } public function ajax_delete_export_job() { self::verify_ajax_request(); $job_id = isset($_POST['job_id']) ? sanitize_text_field(wp_unslash($_POST['job_id'])) : ''; if ($job_id === '') { wp_send_json_error(array('message' => __('Missing job id.', 'ai-eshop-optimizer'))); } global $wpdb; $table = $wpdb->prefix . 'aieo_dmm_export_jobs'; $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE job_id = %s", $job_id), ARRAY_A); if (!$row) { wp_send_json_error(array('message' => __('Job not found.', 'ai-eshop-optimizer'))); } $candidates = array(); $params = !empty($row['params']) ? json_decode($row['params'], true) : null; if (is_array($params)) { foreach (array('path', 'xlsx_path', 'staging_path') as $k) { if (!empty($params[$k])) { $candidates[] = (string) $params[$k]; } } } if (!empty($row['filename'])) { $subdirs = array('orders', 'prices', 'logs', 'exports', 'feeds'); foreach ($subdirs as $sub) { $try = AIEO_DMM::get_upload_path($sub); $candidates[] = trailingslashit($try['path']) . $row['filename']; } } $deleted_files = 0; foreach (array_unique($candidates) as $path) { if ($path !== '' && file_exists($path) && is_file($path)) { if (@unlink($path)) { $deleted_files++; } } } $wpdb->delete($table, array('job_id' => $job_id), array('%s')); wp_send_json_success(array( 'job_id' => $job_id, 'files_removed' => $deleted_files, 'message' => __('Export deleted.', 'ai-eshop-optimizer'), )); } public function ajax_save_enabled_modules() { self::verify_ajax_request(); $raw = isset($_POST['enabled_modules']) && is_array($_POST['enabled_modules']) ? map_deep(wp_unslash($_POST['enabled_modules']), 'sanitize_text_field') : array(); $out = array(); foreach ($raw as $mid => $flag) { $out[sanitize_key($mid)] = !empty($flag); } AIEO_DMM_Settings::update(array('enabled_modules' => $out)); wp_send_json_success(array('message' => __('Modules updated. Reload the page to take effect.', 'ai-eshop-optimizer'))); } private static $iapi_module_status = array( 'pricing' => 'production', 'wishlist' => 'production', 'waitlist' => 'production', 'complementary' => 'production', 'reco' => 'production', 'ratings' => 'production', 'swatches' => 'scaffold', 'sticky_atc' => 'scaffold', 'side_cart' => 'production', 'semantic_tooltips' => 'scaffold', ); public static function is_iapi_enabled($module) { static $cache = array(); $module = sanitize_key((string) $module); if ($module === '') { return false; } if (isset($cache[$module])) { return $cache[$module]; } $override = (string) get_option('aieo_dmm_' . $module . '_iapi_enabled', 'inherit'); $block_default = ($override === 'inherit') && in_array($module, array('ratings', 'wishlist', 'side_cart'), true) && wp_is_block_theme(); $resolved = ($override === 'yes') || $block_default; $status = isset(self::$iapi_module_status[$module]) ? self::$iapi_module_status[$module] : 'unknown'; if ($resolved && $status !== 'production') { $allow_scaffold = (bool) apply_filters('aieo/iapi/allow_scaffold', false, $module, $status); if (!$allow_scaffold) { $resolved = false; if (defined('WP_DEBUG') && WP_DEBUG && !isset($GLOBALS['_aieo_iapi_warned'][$module])) { $GLOBALS['_aieo_iapi_warned'][$module] = true; error_log(sprintf( '[AIEO IAPI] Module "%s" requested IAPI activation but is marked "%s". ' . 'Set add_filter("aieo/iapi/allow_scaffold", "__return_true") to override (dev only).', $module, $status )); } } } $resolved = (bool) apply_filters('aieo/iapi/enabled', $resolved, $module); $cache[$module] = $resolved; return $resolved; } public static function get_iapi_module_statuses() { return self::$iapi_module_status; } public static function is_iapi_master_on() { return (bool) get_option('rooster_iapi_enabled', get_option('roosterx_iapi_enabled', false)); } public static function verify_ajax_request() { check_ajax_referer(self::NONCE_ACTION, 'nonce'); if (!current_user_can(AIEO_CAPABILITY)) { wp_send_json_error(array( 'message' => __('Insufficient permissions.', 'ai-eshop-optimizer'), )); } } public static function pin_capability() { return defined('AIEO_CAPABILITY') ? AIEO_CAPABILITY : 'manage_woocommerce'; } public static function get_upload_path($subdir = '') { $upload = wp_upload_dir(); $base = trailingslashit($upload['basedir']) . 'aieo-dmm'; $url = trailingslashit($upload['baseurl']) . 'aieo-dmm'; if ($subdir !== '') { $subdir = trim($subdir, "/\\"); $base .= '/' . $subdir; $url .= '/' . $subdir; } if (!file_exists($base)) { wp_mkdir_p($base); $index = $base . '/index.html'; if (!file_exists($index)) { file_put_contents($index, ''); } } return array('path' => $base, 'url' => $url); } public static function get_exports_path() { $upload = wp_upload_dir(); $base = trailingslashit($upload['basedir']) . 'exports'; $url = trailingslashit($upload['baseurl']) . 'exports'; if (!file_exists($base)) { wp_mkdir_p($base); $index = $base . '/index.html'; if (!file_exists($index)) { file_put_contents($index, ''); } } return array('path' => $base, 'url' => $url); } }