> */ private array $endpoints = []; /** * Constructor. */ public function __construct() { add_action('init', [$this, 'register_custom_endpoints']); add_filter('woocommerce_account_menu_items', [$this, 'filter_menu_items'], 99); add_filter('woocommerce_get_endpoint_url', [$this, 'filter_logout_url'], 10, 4); add_action('template_redirect', [$this, 'maybe_render_endpoint_template'], 1); // Use priority 15 to ensure the parent menu exists before adding this submenu add_action('admin_menu', [$this, 'register_admin_page'], 15); add_action('admin_init', [$this, 'register_settings']); } /** * Register custom endpoints (rewrite). * * @return void */ public function register_custom_endpoints(): void { foreach ($this->get_endpoints() as $slug => $data) { if (!empty($data['is_custom']) && !empty($data['enabled'])) { add_rewrite_endpoint($slug, EP_ROOT | EP_PAGES); } } } /** * Filter My Account menu items based on config. * * @param array $items Default menu. * * @return array */ public function filter_menu_items(array $items): array { $endpoints = $this->get_endpoints(); $output = []; foreach ($endpoints as $slug => $data) { if (empty($data['enabled'])) { continue; } if (!$this->is_allowed_for_user($data)) { continue; } $label = $data['label'] ?? ($items[$slug] ?? ucfirst(str_replace('-', ' ', $slug))); $position = isset($data['position']) ? (int) $data['position'] : 20; $output[$position . ':' . $slug] = [$slug, $label]; } ksort($output, SORT_NATURAL); $sorted = []; foreach ($output as $item) { [$slug, $label] = $item; $sorted[$slug] = $label; } return $sorted; } /** * Render endpoint template if mapped; otherwise fall back. * * @return void */ public function maybe_render_endpoint_template(): void { if (!function_exists('is_account_page') || !is_account_page()) { return; } if (!class_exists(Elementor_Plugin::class)) { return; } if (isset($_GET['ka_logout_confirm'])) { $this->render_logout_confirm(); return; } $endpoints = $this->get_endpoints(); $current = $this->detect_current_endpoint(array_keys($endpoints)); if (!$current) { return; } $config = $endpoints[$current] ?? []; $template_id = isset($config['template_id']) ? (int) $config['template_id'] : 0; if (!$this->is_allowed_for_user($config)) { return; } if (!$template_id || !king_addons_can_use_pro()) { return; } status_header(200); nocache_headers(); echo ''; exit; } /** * Add confirm flag to logout URL if configured. * * @param string $url URL. * @param string $endpoint Endpoint. * @param string $value Value. * @param string $permalink Permalink. * * @return string */ public function filter_logout_url(string $url, string $endpoint, string $value, string $permalink): string { $endpoints = $this->get_endpoints(); if ('customer-logout' === $endpoint && !empty($endpoints['customer-logout']['confirm'])) { $url = add_query_arg('ka_logout_confirm', '1', $url); } return $url; } /** * Get endpoints config with defaults and filters. * * @return array> */ public function get_endpoints(): array { if (!empty($this->endpoints)) { return $this->endpoints; } $defaults = wc_get_account_menu_items(); $config = get_option(self::OPTION_NAME, []); $endpoints = []; foreach ($defaults as $slug => $label) { $endpoints[$slug] = [ 'label' => $label, 'enabled' => true, 'position' => 20, 'template_id' => 0, 'is_custom' => false, 'confirm' => false, ]; } // Merge saved config. if (is_array($config)) { foreach ($config as $slug => $data) { $endpoints[$slug] = array_merge($endpoints[$slug] ?? [], $data); } } /** * Filter endpoints (add custom, adjust existing). * * @param array> $endpoints Endpoints config. */ $endpoints = apply_filters('king_addons/my_account/endpoints', $endpoints); $this->endpoints = $endpoints; return $this->endpoints; } /** * Detect current endpoint slug. * * @param array $endpoints Endpoint slugs. * * @return string|null */ private function detect_current_endpoint(array $endpoints): ?string { global $wp; foreach ($endpoints as $endpoint) { if (isset($wp->query_vars[$endpoint])) { return $endpoint; } } return null; } /** * Register admin settings page. * * @return void */ public function register_admin_page(): void { add_submenu_page( 'king-addons', esc_html__('My Account Endpoints', 'king-addons'), esc_html__('My Account Endpoints', 'king-addons'), 'manage_options', 'king-addons-myaccount-endpoints', [$this, 'render_admin_page'] ); } /** * Register settings. * * @return void */ public function register_settings(): void { register_setting( 'king_addons_myaccount_endpoints', self::OPTION_NAME, [ 'type' => 'array', 'description' => 'King Addons My Account endpoints config', 'sanitize_callback' => [$this, 'sanitize_endpoints_option'], 'default' => [], ] ); } /** * Sanitize endpoints option (expects JSON string or array). * * @param mixed $value Raw value. * * @return array */ public function sanitize_endpoints_option($value): array { if (is_string($value)) { $decoded = json_decode($value, true); if (is_array($decoded)) { return $decoded; } return []; } return is_array($value) ? $value : []; } /** * Render admin page. * * @return void */ public function render_admin_page(): void { if (!current_user_can('manage_options')) { return; } $option = get_option(self::OPTION_NAME, []); $json = wp_json_encode($option, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); ?>

{label, enabled, position, template_id, is_custom, confirm, roles:[...]}. Custom endpoints require Pro.', 'king-addons'); ?>

'; echo '.ka-logout-confirm{min-height:100vh;display:flex;align-items:center;justify-content:center;background:#f8fafc;padding:24px;box-sizing:border-box;}'; echo '.ka-logout-confirm__card{max-width:420px;width:100%;background:#fff;border:1px solid #e5e7eb;border-radius:12px;box-shadow:0 10px 30px rgba(15,23,42,0.08);padding:28px;text-align:center;}'; echo '.ka-logout-confirm__title{margin:0 0 12px;font-size:24px;font-weight:700;color:#0f172a;}'; echo '.ka-logout-confirm__desc{margin:0 0 20px;font-size:15px;color:#334155;}'; echo '.ka-logout-confirm__actions{display:flex;gap:12px;justify-content:center;flex-wrap:wrap;}'; echo '.ka-logout-confirm__btn{display:inline-flex;align-items:center;justify-content:center;padding:10px 18px;border-radius:8px;border:1px solid transparent;font-weight:600;text-decoration:none;transition:all .15s ease;min-width:120px;}'; echo '.ka-logout-confirm__btn--primary{background:#2563eb;border-color:#2563eb;color:#fff;}'; echo '.ka-logout-confirm__btn--primary:hover{background:#1d4ed8;border-color:#1d4ed8;}'; echo '.ka-logout-confirm__btn--ghost{background:#fff;border-color:#cbd5e1;color:#0f172a;}'; echo '.ka-logout-confirm__btn--ghost:hover{border-color:#94a3b8;}'; echo ''; echo '
'; echo '
'; echo '

' . esc_html__('Confirm logout', 'king-addons') . '

'; echo '

' . esc_html__('Are you sure you want to log out?', 'king-addons') . '

'; echo ''; echo '
'; echo '
'; exit; } /** * Check role-based access. * * @param array $data Endpoint data. * * @return bool */ private function is_allowed_for_user(array $data): bool { if (empty($data['roles']) || !is_array($data['roles'])) { return true; } $user = wp_get_current_user(); if (!$user || empty($user->roles)) { return false; } return (bool) array_intersect($user->roles, $data['roles']); } }