$v) { if (!isset($all[$k]) || '' === trim((string) $all[$k])) { $all[$k] = $v; } } if (null !== self::$all_memo) { self::$all_memo = $all; self::$i18n_filled = true; } return $all; } private static function i18n_map() { return array( 'lowest_price_message_text' => __('Lowest price in the last 30 days: {lowest_price}', 'ai-eshop-optimizer'), 'lowest_price_tooltip_text' => __('The lowest price of this product in the 30 days before the sale started.', 'ai-eshop-optimizer'), 'monthly_offer_label' => __('MONTHLY OFFER', 'ai-eshop-optimizer'), 'new_product_label' => __('NEW', 'ai-eshop-optimizer'), ); } private static function i18n_sources() { return array( 'lowest_price_message_text' => 'Lowest price in the last 30 days: {lowest_price}', 'lowest_price_tooltip_text' => 'The lowest price of this product in the 30 days before the sale started.', 'monthly_offer_label' => 'MONTHLY OFFER', 'new_product_label' => 'NEW', ); } private static function strip_i18n_defaults(array $all) { if (!did_action('init')) { return $all; } $translated = self::i18n_map(); $english = self::i18n_sources(); foreach ($translated as $k => $v) { if (!isset($all[$k]) || !is_string($all[$k])) { continue; } $value = trim($all[$k]); if ($value === $v || (isset($english[$k]) && $value === $english[$k])) { $all[$k] = ''; } } return $all; } public static function flush_memo() { self::$all_memo = null; self::$i18n_filled = false; } public static function chip_colour_value($value, $retired) { $c = sanitize_hex_color(trim((string) $value)); return ($c && 0 !== strcasecmp($c, $retired)) ? $c : ''; } public static function get($key, $default = null) { $all = self::get_all(); if (strpos($key, '.') === false) { return array_key_exists($key, $all) ? $all[$key] : $default; } $parts = explode('.', $key); $node = $all; foreach ($parts as $part) { if (!is_array($node) || !array_key_exists($part, $node)) { return $default; } $node = $node[$part]; } return $node; } public static function update(array $patch) { $current = self::get_all(); $merged = array_replace_recursive($current, $patch); $merged = self::sanitize($merged); $merged = self::strip_i18n_defaults($merged); $saved = update_option(self::OPTION_KEY, $merged, false); self::flush_memo(); return $saved; } public static function replace(array $new) { $new = self::sanitize(array_replace_recursive(self::defaults(), $new)); $new = self::strip_i18n_defaults($new); $saved = update_option(self::OPTION_KEY, $new, false); self::flush_memo(); return $saved; } public static function defaults() { $legacy = get_option('om_role_pricing_settings', array()); $legacy_roles = (is_array($legacy) && !empty($legacy['enabled_roles']) && is_array($legacy['enabled_roles'])) ? array_values(array_map('sanitize_key', $legacy['enabled_roles'])) : array(); return array( 'enabled_modules' => array( 'role_pricing' => true, 'brand_discounts' => true, 'brand_gifts' => true, 'loyalty_classes' => true, 'free_shipping' => true, 'side_cart' => false, 'sticky_atc' => false, 'product_needs' => true, 'branch_scanner' => true, 'orders_export' => true, 'stock_import' => true, 'price_export' => true, 'marketplace_feeds' => true, 'erp_bridge' => true, 'complementary' => true, 'navigator' => true, 'category_extras' => true, ), 'enabled_roles' => $legacy_roles, 'seo' => array( 'enabled' => false, 'separator' => '-', ), 'recipe' => array( 'role_pricing' => 'active', 'brand_discount' => 'stack_multiplicative', 'loyalty_class' => 'stack_multiplicative', 'brand_gift' => 'over_and_above', 'no_free_shipping_gate' => 'hard', ), 'show_login_message' => !empty($legacy['show_login_message']), 'login_message_text' => isset($legacy['login_message_text']) ? (string) $legacy['login_message_text'] : '', 'login_message_archive' => isset($legacy['login_message_archive']) ? (string) $legacy['login_message_archive'] : '', 'login_message_css_class' => isset($legacy['login_message_css_class']) ? (string) $legacy['login_message_css_class'] : '', 'mini_cart_badge_icon' => '', 'mini_cart_badge_text' => '', 'mini_cart_badge_alt' => '', 'lowest_price_message_enabled' => false, 'lowest_price_message_text' => '', 'lowest_price_tooltip_text' => '', 'lowest_price_message_font_size' => '0.75rem', 'price_layout' => 'inline', 'price_regular_font_size' => '', 'price_regular_color' => '', 'price_sale_font_size' => '', 'price_sale_color' => '', 'price_sale_bold' => false, 'price_regular_font_size_mobile' => '', 'price_sale_font_size_mobile' => '', 'price_archive_regular_font_size' => '', 'price_archive_regular_font_size_mobile' => '', 'price_archive_sale_font_size' => '', 'price_archive_sale_font_size_mobile' => '', 'price_separator_enabled' => false, 'price_separator_spacing' => '16px', 'price_separator_thickness' => '1px', 'price_separator_color' => '#e0e0e0', 'wpdatatables_table_id' => isset($legacy['wpdatatables_table_id']) ? (int) $legacy['wpdatatables_table_id'] : 0, 'archive_badge_position' => 'image_top_left', 'bundle_mode' => 'full', 'pricing_mode' => 'reactive', 'apply_recipe_to_main_price' => false, 'side_cart_enabled' => false, 'side_cart_open_on_add' => false, 'side_cart_quick_view_enabled' => false, 'side_cart_heading' => '', 'side_cart_left_heading' => '', 'side_cart_tab_bag' => '', 'side_cart_tab_viewed' => '', 'side_cart_continue' => '', 'side_cart_maylike_title' => '', 'side_cart_subtotal_label' => '', 'side_cart_coupon_link' => '', 'side_cart_btn_cart' => '', 'side_cart_btn_checkout' => '', 'side_cart_btn_addbag' => '', 'side_cart_rv_empty' => '', 'side_cart_quick_view_label'=> '', 'side_cart_qv_tab' => '', 'side_cart_qv_view_product' => '', 'side_cart_tab_favorites' => '', 'side_cart_fav_empty' => '', 'side_cart_rv_atc' => '', 'side_cart_in_cart_label' => '', 'side_cart_card_border_enabled' => true, 'side_cart_card_border_color' => '#c0a37b', 'side_cart_card_border_hover' => '#a88a63', 'side_cart_progress_text' => 'on_bar', 'navigator_excluded_terms' => '', 'navigator_daily_enabled' => true, 'navigator_daily_time' => '03:00', 'navigator_border_width' => 1, 'navigator_border_radius' => 12, 'navigator_border_color' => '#e5e7eb', 'category_extras_chips_enabled' => true, 'category_extras_chips_border_width' => 1, 'category_extras_chips_border_radius' => 0, 'category_extras_chips_border_color' => '', 'category_extras_chips_text_color' => '', 'category_extras_chips_padding_y' => 8, 'category_extras_chips_padding_x' => 22, 'category_extras_chips_gap_y' => 30, 'category_extras_chips_gap_x' => 35, 'category_extras_chips_mobile_slider' => false, 'new_product_days' => 60, 'new_product_label' => '', 'new_product_badge_bg' => '#1a1a1a', 'new_product_badge_fg' => '#ffffff', 'monthly_offer_label' => '', 'monthly_offer_badge_bg' => '#F46767', 'monthly_offer_badge_fg' => '#ffffff', 'contact_quiet_start' => '22:00', 'contact_quiet_end' => '09:00', 'gf_registration_form_id' => 0, 'gf_update_form_id' => 0, 'gf_checkbox_field_id' => 11, 'gf_role_map' => array(), 'gf_professionals_page_id' => 0, 'gf_prompt_form_id' => 0, 'gf_prompt_pro_field_id' => 51, 'gf_prompt_member_field_id' => 48, 'gf_intro_logged_in' => '', 'gf_intro_logged_out' => '', 'pro_signup_form' => '', 'pro_signup_role_professional' => '', 'pro_signup_role_student' => '', 'repeat_order_enabled' => true, 'repeat_order_show_on_order_list' => true, 'repeat_order_create_order_note' => true, 'repeat_order_reactivate_order_again' => false, 'repeat_order_show_reorder_from_column' => false, 'repeat_order_cart_notice' => '', 'repeat_order_button_label' => '', 'repeat_order_allowed_statuses' => array( 'completed', 'processing' ), ); } public static function sanitize(array $data) { $defaults = self::defaults(); foreach (array('contact_quiet_start', 'contact_quiet_end') as $qk) { $qv = isset($data[$qk]) ? trim((string) $data[$qk]) : $defaults[$qk]; $data[$qk] = (preg_match('/^(\d{1,2}):(\d{2})$/', $qv, $qm) && (int) $qm[1] <= 23 && (int) $qm[2] <= 59) ? sprintf('%02d:%02d', (int) $qm[1], (int) $qm[2]) : $defaults[$qk]; } $modules = isset($data['enabled_modules']) && is_array($data['enabled_modules']) ? $data['enabled_modules'] : array(); foreach ($defaults['enabled_modules'] as $mid => $_) { $modules[$mid] = !empty($modules[$mid]); } $data['enabled_modules'] = $modules; $roles = isset($data['enabled_roles']) && is_array($data['enabled_roles']) ? $data['enabled_roles'] : array(); $clean_roles = array(); foreach ($roles as $role) { $slug = sanitize_key($role); if ($slug !== '' && $slug !== 'administrator') { $clean_roles[$slug] = true; } } $data['enabled_roles'] = array_keys($clean_roles); $recipe_options = array( 'role_pricing' => array('active', 'off'), 'brand_discount' => array('stack_multiplicative', 'stack_additive', 'replace_role', 'off'), 'loyalty_class' => array('stack_multiplicative', 'stack_additive', 'replace_all', 'off'), 'brand_gift' => array('over_and_above', 'off'), 'no_free_shipping_gate' => array('hard', 'soft', 'off'), ); $recipe = isset($data['recipe']) && is_array($data['recipe']) ? $data['recipe'] : array(); foreach ($recipe_options as $k => $allowed) { $v = isset($recipe[$k]) ? (string) $recipe[$k] : $defaults['recipe'][$k]; $recipe[$k] = in_array($v, $allowed, true) ? $v : $defaults['recipe'][$k]; } $data['recipe'] = $recipe; $data['show_login_message'] = !empty($data['show_login_message']); $data['login_message_text'] = isset($data['login_message_text']) ? wp_kses_post((string) $data['login_message_text']) : ''; $data['login_message_archive'] = isset($data['login_message_archive']) ? wp_kses_post((string) $data['login_message_archive']) : ''; $data['login_message_css_class'] = isset($data['login_message_css_class']) ? sanitize_html_class((string) $data['login_message_css_class'], '') : ''; $data['lowest_price_message_enabled'] = !empty($data['lowest_price_message_enabled']); $data['lowest_price_message_text'] = isset($data['lowest_price_message_text']) ? wp_kses_post((string) $data['lowest_price_message_text']) : $defaults['lowest_price_message_text']; $data['lowest_price_tooltip_text'] = isset($data['lowest_price_tooltip_text']) ? wp_kses_post((string) $data['lowest_price_tooltip_text']) : $defaults['lowest_price_tooltip_text']; $lp_sz = isset($data['lowest_price_message_font_size']) ? trim((string) $data['lowest_price_message_font_size']) : ''; $data['lowest_price_message_font_size'] = preg_match('/^[0-9]*\.?[0-9]+\s*(rem|em|px|%|vw|vh|pt)$/i', $lp_sz) ? $lp_sz : $defaults['lowest_price_message_font_size']; $p_layout = isset($data['price_layout']) ? sanitize_key((string) $data['price_layout']) : 'inline'; $data['price_layout'] = in_array($p_layout, array('inline', 'stacked_sale_first', 'stacked_regular_first'), true) ? $p_layout : 'inline'; foreach (array( 'price_regular_font_size', 'price_sale_font_size', 'price_regular_font_size_mobile', 'price_sale_font_size_mobile', 'price_archive_regular_font_size', 'price_archive_regular_font_size_mobile', 'price_archive_sale_font_size', 'price_archive_sale_font_size_mobile', 'price_separator_spacing', 'price_separator_thickness', ) as $pfk) { $pv = isset($data[$pfk]) ? trim((string) $data[$pfk]) : ''; $data[$pfk] = ($pv === '' || preg_match('/^[0-9]*\.?[0-9]+\s*(rem|em|px|%|vw|vh|pt)$/i', $pv)) ? $pv : ''; } foreach (array('price_regular_color', 'price_sale_color', 'price_separator_color') as $pck) { $pcv = isset($data[$pck]) ? (string) $data[$pck] : ''; $data[$pck] = class_exists('AIEO_Admin_Color') ? AIEO_Admin_Color::sanitize($pcv) : ((string) sanitize_hex_color($pcv)); } $data['price_sale_bold'] = !empty($data['price_sale_bold']); $data['price_separator_enabled'] = !empty($data['price_separator_enabled']); $data['mini_cart_badge_icon'] = isset($data['mini_cart_badge_icon']) ? wp_kses_post((string) $data['mini_cart_badge_icon']) : ''; $data['mini_cart_badge_text'] = isset($data['mini_cart_badge_text']) ? sanitize_text_field((string) $data['mini_cart_badge_text']) : ''; $data['mini_cart_badge_alt'] = isset($data['mini_cart_badge_alt']) ? wp_kses_post((string) $data['mini_cart_badge_alt']) : ''; $data['side_cart_card_border_enabled'] = !isset($data['side_cart_card_border_enabled']) ? true : !empty($data['side_cart_card_border_enabled']); $data['side_cart_card_border_color'] = isset($data['side_cart_card_border_color']) ? (sanitize_hex_color((string) $data['side_cart_card_border_color']) ?: '#c0a37b') : '#c0a37b'; $data['side_cart_card_border_hover'] = isset($data['side_cart_card_border_hover']) ? (sanitize_hex_color((string) $data['side_cart_card_border_hover']) ?: '#a88a63') : '#a88a63'; $data['side_cart_progress_text'] = (isset($data['side_cart_progress_text']) && 'above' === $data['side_cart_progress_text']) ? 'above' : 'on_bar'; $data['wpdatatables_table_id'] = isset($data['wpdatatables_table_id']) ? absint($data['wpdatatables_table_id']) : 0; $bundle_mode_allowed = array('full', 'rec_only'); $mode = isset($data['bundle_mode']) ? sanitize_key((string) $data['bundle_mode']) : 'full'; $data['bundle_mode'] = in_array($mode, $bundle_mode_allowed, true) ? $mode : 'full'; $pricing_mode_allowed = array('reactive', 'precomputed'); $pmode = isset($data['pricing_mode']) ? sanitize_key((string) $data['pricing_mode']) : 'reactive'; $data['pricing_mode'] = in_array($pmode, $pricing_mode_allowed, true) ? $pmode : 'reactive'; $data['apply_recipe_to_main_price'] = !empty($data['apply_recipe_to_main_price']); $data['side_cart_enabled'] = !empty($data['side_cart_enabled']); $data['side_cart_open_on_add'] = !empty($data['side_cart_open_on_add']); $data['side_cart_quick_view_enabled'] = !empty($data['side_cart_quick_view_enabled']); $data['side_cart_heading'] = isset($data['side_cart_heading']) ? sanitize_text_field((string) $data['side_cart_heading']) : ''; $data['side_cart_left_heading'] = isset($data['side_cart_left_heading']) ? sanitize_text_field((string) $data['side_cart_left_heading']) : ''; $label_keys = array( 'side_cart_tab_bag', 'side_cart_tab_viewed', 'side_cart_continue', 'side_cart_maylike_title', 'side_cart_subtotal_label', 'side_cart_coupon_link', 'side_cart_btn_cart', 'side_cart_btn_checkout', 'side_cart_btn_addbag', 'side_cart_rv_empty', 'side_cart_quick_view_label', 'side_cart_qv_view_product', 'side_cart_qv_tab', 'side_cart_tab_favorites', 'side_cart_fav_empty', 'side_cart_rv_atc', 'side_cart_in_cart_label', ); foreach ($label_keys as $k) { $data[$k] = isset($data[$k]) ? sanitize_text_field((string) $data[$k]) : ''; } $data['navigator_excluded_terms'] = isset($data['navigator_excluded_terms']) ? sanitize_textarea_field((string) $data['navigator_excluded_terms']) : ''; $data['navigator_daily_enabled'] = !isset($data['navigator_daily_enabled']) ? true : !empty($data['navigator_daily_enabled']); $time = isset($data['navigator_daily_time']) ? (string) $data['navigator_daily_time'] : '03:00'; $data['navigator_daily_time'] = preg_match('/^([01]\d|2[0-3]):[0-5]\d$/', $time) ? $time : '03:00'; $data['category_extras_chips_enabled'] = !isset($data['category_extras_chips_enabled']) ? true : !empty($data['category_extras_chips_enabled']); $clamp_int = static function ($val, $min, $max, $default) { $n = is_numeric($val) ? (int) $val : $default; return max($min, min($max, $n)); }; $hex = static function ($val, $default) { return sanitize_hex_color((string) $val) ?: $default; }; $data['category_extras_chips_border_width'] = $clamp_int($data['category_extras_chips_border_width'] ?? 1, 0, 10, 1); $data['category_extras_chips_border_radius'] = $clamp_int($data['category_extras_chips_border_radius'] ?? 0, 0, 60, 0); $data['category_extras_chips_border_color'] = self::chip_colour_value($data['category_extras_chips_border_color'] ?? '', '#F46767'); $data['category_extras_chips_text_color'] = self::chip_colour_value($data['category_extras_chips_text_color'] ?? '', '#1a1a1a'); $data['category_extras_chips_padding_y'] = $clamp_int($data['category_extras_chips_padding_y'] ?? 8, 0, 64, 8); $data['category_extras_chips_padding_x'] = $clamp_int($data['category_extras_chips_padding_x'] ?? 22, 0, 96, 22); $data['category_extras_chips_gap_y'] = $clamp_int($data['category_extras_chips_gap_y'] ?? 30, 0, 96, 30); $data['category_extras_chips_gap_x'] = $clamp_int($data['category_extras_chips_gap_x'] ?? 35, 0, 96, 35); $data['category_extras_chips_mobile_slider'] = !empty($data['category_extras_chips_mobile_slider']); $data['navigator_border_width'] = $clamp_int($data['navigator_border_width'] ?? 1, 0, 8, 1); $data['navigator_border_radius'] = $clamp_int($data['navigator_border_radius'] ?? 12, 0, 40, 12); $data['navigator_border_color'] = $hex($data['navigator_border_color'] ?? '#e5e7eb', '#e5e7eb'); $data['gf_registration_form_id'] = isset($data['gf_registration_form_id']) ? absint($data['gf_registration_form_id']) : 0; $data['gf_update_form_id'] = isset($data['gf_update_form_id']) ? absint($data['gf_update_form_id']) : 0; $data['gf_checkbox_field_id'] = isset($data['gf_checkbox_field_id']) ? max(1, absint($data['gf_checkbox_field_id'])) : 11; $data['gf_professionals_page_id'] = isset($data['gf_professionals_page_id']) ? absint($data['gf_professionals_page_id']) : 0; $data['gf_prompt_form_id'] = isset($data['gf_prompt_form_id']) ? absint($data['gf_prompt_form_id']) : 0; $data['gf_prompt_pro_field_id'] = isset($data['gf_prompt_pro_field_id']) ? max(1, absint($data['gf_prompt_pro_field_id'])) : 51; $data['gf_prompt_member_field_id'] = isset($data['gf_prompt_member_field_id']) ? max(1, absint($data['gf_prompt_member_field_id'])) : 48; $data['gf_intro_logged_in'] = isset($data['gf_intro_logged_in']) ? wp_kses_post((string) $data['gf_intro_logged_in']) : ''; $data['gf_intro_logged_out'] = isset($data['gf_intro_logged_out']) ? wp_kses_post((string) $data['gf_intro_logged_out']) : ''; $data['pro_signup_form'] = isset($data['pro_signup_form']) && in_array($data['pro_signup_form'], array('aieo', 'gravity'), true) ? (string) $data['pro_signup_form'] : ''; foreach (array('pro_signup_role_professional', 'pro_signup_role_student') as $k) { $data[$k] = isset($data[$k]) ? sanitize_key((string) $data[$k]) : ''; } $raw_map = isset($data['gf_role_map']) && is_array($data['gf_role_map']) ? $data['gf_role_map'] : array(); $allowed_slugs = isset($data['enabled_roles']) && is_array($data['enabled_roles']) ? $data['enabled_roles'] : array(); $clean_map = array(); foreach ($raw_map as $slug => $value) { $slug = sanitize_key((string) $slug); if ($slug === '' || !in_array($slug, $allowed_slugs, true)) { continue; } $int_val = absint($value); if ($int_val > 0) { $clean_map[$slug] = $int_val; } } $data['gf_role_map'] = $clean_map; $data['repeat_order_enabled'] = !empty($data['repeat_order_enabled']); $data['repeat_order_show_on_order_list'] = !empty($data['repeat_order_show_on_order_list']); $data['repeat_order_create_order_note'] = !empty($data['repeat_order_create_order_note']); $data['repeat_order_reactivate_order_again'] = !empty($data['repeat_order_reactivate_order_again']); $data['repeat_order_show_reorder_from_column'] = !empty($data['repeat_order_show_reorder_from_column']); $data['repeat_order_cart_notice'] = isset($data['repeat_order_cart_notice']) ? sanitize_text_field((string) $data['repeat_order_cart_notice']) : ''; $data['repeat_order_button_label'] = isset($data['repeat_order_button_label']) ? sanitize_text_field((string) $data['repeat_order_button_label']) : ''; $valid_ro_statuses = array('pending', 'processing', 'on-hold', 'completed', 'cancelled', 'refunded', 'failed'); $ro_in = (isset($data['repeat_order_allowed_statuses']) && is_array($data['repeat_order_allowed_statuses'])) ? $data['repeat_order_allowed_statuses'] : array(); $ro_clean = array(); foreach ($ro_in as $st) { $st = sanitize_key((string) $st); if (in_array($st, $valid_ro_statuses, true)) { $ro_clean[] = $st; } } $data['repeat_order_allowed_statuses'] = !empty($ro_clean) ? $ro_clean : array('completed', 'processing'); return $data; } public static function is_module_enabled($module_id) { $modules = self::get('enabled_modules', array()); return !empty($modules[$module_id]); } public static function sync_legacy_role_pricing_option() { $all = self::get_all(); $legacy = get_option('om_role_pricing_settings', array()); if (!is_array($legacy)) { $legacy = array(); } $legacy['enabled_roles'] = $all['enabled_roles']; $legacy['show_login_message'] = !empty($all['show_login_message']); $legacy['login_message_text'] = $all['login_message_text']; $legacy['login_message_archive'] = $all['login_message_archive']; $legacy['login_message_css_class'] = $all['login_message_css_class']; $legacy['wpdatatables_table_id'] = $all['wpdatatables_table_id']; update_option('om_role_pricing_settings', $legacy, false); } } if (function_exists('add_action') && !has_action('update_option_' . AIEO_DMM_Settings::OPTION_KEY, array('AIEO_DMM_Settings', 'flush_memo'))) { add_action('update_option_' . AIEO_DMM_Settings::OPTION_KEY, array('AIEO_DMM_Settings', 'flush_memo')); add_action('add_option_' . AIEO_DMM_Settings::OPTION_KEY, array('AIEO_DMM_Settings', 'flush_memo')); add_action('delete_option_' . AIEO_DMM_Settings::OPTION_KEY, array('AIEO_DMM_Settings', 'flush_memo')); }