| @@ -24,8 +24,9 @@ | ||
| 24 | 24 | add_action('admin_menu', [$this, 'addUpgradeMenu'], 9999999999); // Highest priority to add at the very end |
| 25 | 25 | |
| 26 | 26 | // Reorder submenu items after ALL entries are registered. |
| 27 | 27 | add_action('admin_menu', [$this, 'reorderKingAddonsSubmenu'], 1000000000); |
| 28 | + add_action('admin_menu', [$this, 'reorderWooBuilderSubmenu'], 1000000000); | |
| 28 | 29 | |
| 29 | 30 | add_action('admin_init', [$this, 'createSettings']); |
| 30 | 31 | add_action('admin_init', [$this, 'createAiSettings']); |
| 31 | 32 | |
| @@ -90,31 +91,8 @@ | ||
| 90 | 91 | |
| 91 | 92 | // Get options for extension toggle checks (before any extension checks) |
| 92 | 93 | $options = get_option('king_addons_options', []); |
| 93 | 94 | |
| 94 | - // Check Wishlist extension toggle | |
| 95 | - $wishlist_enabled = (!isset($options['ext_wishlist']) || $options['ext_wishlist'] === 'enabled') | |
| 96 | - && (defined('KING_ADDONS_EXT_WISHLIST') ? KING_ADDONS_EXT_WISHLIST : true); | |
| 97 | - if ($wishlist_enabled) { | |
| 98 | - add_submenu_page( | |
| 99 | - 'king-addons', | |
| 100 | - esc_html__('Wishlist', 'king-addons'), | |
| 101 | - esc_html__('Wishlist', 'king-addons'), | |
| 102 | - 'manage_options', | |
| 103 | - 'king-addons-wishlist', | |
| 104 | - [$this, 'renderWishlistPage'] | |
| 105 | - ); | |
| 106 | - | |
| 107 | - add_submenu_page( | |
| 108 | - 'king-addons', | |
| 109 | - esc_html__('Wishlist Analytics', 'king-addons'), | |
| 110 | - esc_html__('Wishlist Analytics', 'king-addons'), | |
| 111 | - 'manage_options', | |
| 112 | - 'king-addons-wishlist-analytics', | |
| 113 | - [$this, 'renderWishlistAnalyticsPage'] | |
| 114 | - ); | |
| 115 | - } | |
| 116 | - | |
| 117 | 95 | // Check Cookie / Consent Bar extension toggle |
| 118 | 96 | $cookie_consent_enabled = !isset($options['ext_cookie-consent']) || $options['ext_cookie-consent'] === 'enabled'; |
| 119 | 97 | if ($cookie_consent_enabled && (defined('KING_ADDONS_EXT_COOKIE_CONSENT') ? KING_ADDONS_EXT_COOKIE_CONSENT : true) && class_exists('King_Addons\Cookie_Consent')) { |
| 120 | 98 | add_submenu_page( |
| @@ -216,19 +194,38 @@ | ||
| 216 | 194 | if ($popup_builder_enabled && (defined('KING_ADDONS_EXT_POPUP_BUILDER') ? KING_ADDONS_EXT_POPUP_BUILDER : true) && class_exists('King_Addons\Popup_Builder')) { |
| 217 | 195 | self::showPopupBuilder(); |
| 218 | 196 | } |
| 219 | 197 | |
| 220 | - // Check WooCommerce Builder extension toggle | |
| 221 | - $woo_builder_enabled = !isset($options['ext_woo-builder']) || $options['ext_woo-builder'] === 'enabled'; | |
| 222 | - if ( | |
| 223 | - $woo_builder_enabled | |
| 224 | - && (defined('KING_ADDONS_EXT_WOO_BUILDER') ? KING_ADDONS_EXT_WOO_BUILDER : true) | |
| 225 | - && class_exists('WooCommerce') | |
| 226 | - && function_exists('WC') | |
| 227 | - ) { | |
| 198 | + // WooCommerce Builder is a top-level menu. Store screens hang off it | |
| 199 | + // when it exists; otherwise they stay under King Addons. Page slugs | |
| 200 | + // do not change. | |
| 201 | + if (king_addons_woo_builder_menu_is_active()) { | |
| 228 | 202 | $this->showWooBuilder(); |
| 229 | 203 | } |
| 230 | 204 | |
| 205 | + $wishlist_enabled = (!isset($options['ext_wishlist']) || $options['ext_wishlist'] === 'enabled') | |
| 206 | + && (defined('KING_ADDONS_EXT_WISHLIST') ? KING_ADDONS_EXT_WISHLIST : true); | |
| 207 | + if ($wishlist_enabled) { | |
| 208 | + $woo_parent = king_addons_woo_admin_parent_slug(); | |
| 209 | + add_submenu_page( | |
| 210 | + $woo_parent, | |
| 211 | + esc_html__('Wishlist', 'king-addons'), | |
| 212 | + esc_html__('Wishlist', 'king-addons'), | |
| 213 | + 'manage_options', | |
| 214 | + 'king-addons-wishlist', | |
| 215 | + [$this, 'renderWishlistPage'] | |
| 216 | + ); | |
| 217 | + | |
| 218 | + add_submenu_page( | |
| 219 | + $woo_parent, | |
| 220 | + esc_html__('Wishlist Analytics', 'king-addons'), | |
| 221 | + esc_html__('Wishlist Analytics', 'king-addons'), | |
| 222 | + 'manage_options', | |
| 223 | + 'king-addons-wishlist-analytics', | |
| 224 | + [$this, 'renderWishlistAnalyticsPage'] | |
| 225 | + ); | |
| 226 | + } | |
| 227 | + | |
| 231 | 228 | $menu['54.8'] = array( '', 'read', 'separator-king-addons-bottom', '', 'wp-menu-separator elementor' ); |
| 232 | 229 | |
| 233 | 230 | } |
| 234 | 231 | |
| @@ -287,8 +284,61 @@ | ||
| 287 | 284 | |
| 288 | 285 | $submenu['king-addons'] = $items; |
| 289 | 286 | } |
| 290 | 287 | |
| 288 | + /** | |
| 289 | + * WooCommerce Builder submenu: | |
| 290 | + * 1) Dashboard (same slug as the top-level page) | |
| 291 | + * 2) Wishlist | |
| 292 | + * 3) Wishlist Analytics | |
| 293 | + * 4) Free Shipping Bar | |
| 294 | + * 5) Sticky Add To Cart | |
| 295 | + * 6) My Account Endpoints | |
| 296 | + */ | |
| 297 | + public function reorderWooBuilderSubmenu(): void | |
| 298 | + { | |
| 299 | + global $submenu; | |
| 300 | + | |
| 301 | + if (!is_array($submenu) || empty($submenu['king-addons-woo-builder']) || !is_array($submenu['king-addons-woo-builder'])) { | |
| 302 | + return; | |
| 303 | + } | |
| 304 | + | |
| 305 | + $priorityBySlug = [ | |
| 306 | + 'king-addons-woo-builder' => 0, | |
| 307 | + 'king-addons-wishlist' => 1, | |
| 308 | + 'king-addons-wishlist-analytics' => 2, | |
| 309 | + 'king-addons-free-shipping-bar' => 3, | |
| 310 | + 'king-addons-sticky-add-to-cart' => 4, | |
| 311 | + 'king-addons-myaccount-endpoints' => 5, | |
| 312 | + ]; | |
| 313 | + | |
| 314 | + $items = $submenu['king-addons-woo-builder']; | |
| 315 | + | |
| 316 | + usort($items, static function ($a, $b) use ($priorityBySlug): int { | |
| 317 | + $aSlug = isset($a[2]) ? (string) $a[2] : ''; | |
| 318 | + $bSlug = isset($b[2]) ? (string) $b[2] : ''; | |
| 319 | + | |
| 320 | + $aPriority = array_key_exists($aSlug, $priorityBySlug) ? $priorityBySlug[$aSlug] : 9999; | |
| 321 | + $bPriority = array_key_exists($bSlug, $priorityBySlug) ? $priorityBySlug[$bSlug] : 9999; | |
| 322 | + | |
| 323 | + if ($aPriority !== $bPriority) { | |
| 324 | + return $aPriority <=> $bPriority; | |
| 325 | + } | |
| 326 | + | |
| 327 | + $aLabel = isset($a[0]) ? wp_strip_all_tags((string) $a[0]) : ''; | |
| 328 | + $bLabel = isset($b[0]) ? wp_strip_all_tags((string) $b[0]) : ''; | |
| 329 | + | |
| 330 | + $cmp = strcasecmp($aLabel, $bLabel); | |
| 331 | + if ($cmp !== 0) { | |
| 332 | + return $cmp; | |
| 333 | + } | |
| 334 | + | |
| 335 | + return strcasecmp($aSlug, $bSlug); | |
| 336 | + }); | |
| 337 | + | |
| 338 | + $submenu['king-addons-woo-builder'] = $items; | |
| 339 | + } | |
| 340 | + | |
| 291 | 341 | function addUpgradeMenu(): void |
| 292 | 342 | { |
| 293 | 343 | // Don't add menu if Freemius is showing opt-in/activation |
| 294 | 344 | $fs = king_addons_freemius(); |
| @@ -343,8 +393,19 @@ | ||
| 343 | 393 | [$this, 'renderWooBuilderPage'], |
| 344 | 394 | 'dashicons-cart', |
| 345 | 395 | 54.5 |
| 346 | 396 | ); |
| 397 | + | |
| 398 | + // Same slug as the parent so the first submenu item is "Dashboard" | |
| 399 | + // instead of repeating "WooCommerce Builder". | |
| 400 | + add_submenu_page( | |
| 401 | + 'king-addons-woo-builder', | |
| 402 | + esc_html__('WooCommerce Builder', 'king-addons'), | |
| 403 | + esc_html__('Dashboard', 'king-addons'), | |
| 404 | + 'manage_options', | |
| 405 | + 'king-addons-woo-builder', | |
| 406 | + [$this, 'renderWooBuilderPage'] | |
| 407 | + ); | |
| 347 | 408 | } |
| 348 | 409 | |
| 349 | 410 | /** |
| 350 | 411 | * Render WooCommerce Builder admin page. |
| @@ -660,8 +721,9 @@ | ||
| 660 | 721 | 'button_added_text' => sanitize_text_field($settings['button_added_text'] ?? $defaults['button_added_text']), |
| 661 | 722 | 'button_display_mode' => in_array($settings['button_display_mode'] ?? 'icon_text', ['icon', 'icon_text'], true) ? $settings['button_display_mode'] : $defaults['button_display_mode'], |
| 662 | 723 | 'button_position' => in_array($settings['button_position'] ?? 'after_add_to_cart', ['before_add_to_cart', 'after_add_to_cart'], true) ? $settings['button_position'] : $defaults['button_position'], |
| 663 | 724 | 'show_in_archives' => !empty($settings['show_in_archives']), |
| 725 | + 'show_on_single' => !empty($settings['show_on_single']), | |
| 664 | 726 | 'wishlist_columns' => array_values( |
| 665 | 727 | array_intersect( |
| 666 | 728 | (array) ($settings['wishlist_columns'] ?? []), |
| 667 | 729 | ['image', 'title', 'price', 'stock', 'notes', 'add_to_cart', 'remove'] |
| @@ -993,9 +1055,10 @@ | ||
| 993 | 1055 | public function sanitizeAiSettings(array $input): array |
| 994 | 1056 | { |
| 995 | 1057 | $sanitized = []; |
| 996 | 1058 | |
| 997 | - $sanitized['ai_provider'] = AI_Provider::normalizeProvider($input['ai_provider'] ?? AI_Provider::OPENAI); | |
| 1059 | + // An absent value resolves to the default rather than being pinned to OpenAI. | |
| 1060 | + $sanitized['ai_provider'] = AI_Provider::normalizeProvider($input['ai_provider'] ?? ''); | |
| 998 | 1061 | |
| 999 | 1062 | $sanitized['openai_api_key'] = isset($input['openai_api_key']) |
| 1000 | 1063 | ? sanitize_text_field($input['openai_api_key']) |
| 1001 | 1064 | : ''; |