parent_base || strpos( $screen->id, 'forumax' ) !== false ) ) { $classes .= ' forumax-admin-page'; } return $classes; } /** * Hide bbPress CPT top-level menus so Forumax can add them explicitly. * * @param array $args Post type registration arguments. * * @return array */ public function hide_bbp_cpt_top_level_menu( $args ) { return $this->set_cpt_menu_parent( $args ); } /** * Register the Forumax Admin Menu. */ public function forumax_admin_menu() { $capability = 'manage_options'; $show_classic_post_types = (bool) forumax_get_opt( 'is_bbp_post_types_hidden', false ); // 1. Root Menu add_menu_page( esc_html__( 'Forumax', 'forumax' ), esc_html__( 'Forumax', 'forumax' ), $capability, 'forumax', [ $this, 'forumax_dashboard_page' ], 'dashicons-groups', 6 ); // 2. Dashboard & Builder add_submenu_page( 'forumax', esc_html__( 'Dashboard', 'forumax' ), esc_html__( 'Dashboard', 'forumax' ), $capability, 'forumax', [ $this, 'forumax_dashboard_page' ] ); add_submenu_page( 'forumax', esc_html__( 'Forum Builder', 'forumax' ), esc_html__( 'Forum Builder', 'forumax' ), $capability, 'forumax-builder', [ $this, 'forumax_plugin_page' ] ); // CPT top-level entries are suppressed via the bbp_register_*_post_type filters // in __construct() (show_in_menu = false), so no removal call is needed here. if ( $show_classic_post_types ) { // --- SEPARATOR 1 (Before Content Management) --- $this->add_frmx_separator( '1' ); // 3. Post Type utility links. $cpts = [ 'forum' => esc_html__( 'Forums', 'forumax' ), 'topic' => esc_html__( 'Topics', 'forumax' ), 'reply' => esc_html__( 'Replies', 'forumax' ), ]; foreach ( $cpts as $pt => $label ) { $post_type_object = get_post_type_object( $pt ); if ( empty( $post_type_object ) ) { continue; } $edit_cap = $post_type_object->cap->edit_posts; $create_cap = isset( $post_type_object->cap->create_posts ) ? $post_type_object->cap->create_posts : $edit_cap; // Top item: the CPT list page (e.g. edit.php?post_type=forum). add_submenu_page( 'forumax', $label, $label, $edit_cap, "edit.php?post_type={$pt}" ); // Indented child: Add New. add_submenu_page( 'forumax', esc_html__( 'Add New ', 'forumax' ) . $label, '    ' . esc_html__( 'Add New', 'forumax' ), $create_cap, "post-new.php?post_type={$pt}&is_child=true" ); // Indented children: taxonomy pages (e.g. topic tags). $taxs = get_object_taxonomies( $pt, 'objects' ); foreach ( $taxs as $t_slug => $t_obj ) { if ( ! $t_obj->show_ui ) { continue; } add_submenu_page( 'forumax', esc_html( $t_obj->labels->name ), '    ' . esc_html( $t_obj->labels->name ), $t_obj->cap->manage_terms, "edit-tags.php?taxonomy={$t_slug}&post_type={$pt}&is_child=true" ); } // Separator after each CPT block except the last (Separator 2 closes the section). if ( 'reply' !== $pt ) { $this->add_frmx_separator( "after-{$pt}" ); } } // --- SEPARATOR 2 (Before Utilities) --- $this->add_frmx_separator( '2' ); } // 4. Utility Pages add_submenu_page( 'forumax', esc_html__( 'Setup Wizard', 'forumax' ), esc_html__( 'Setup Wizard', 'forumax' ), $capability, 'forumax-setup', [ $this, 'forumax_setup_wizard_page' ] ); remove_submenu_page( 'forumax', 'forumax-settings' ); add_submenu_page( 'forumax', esc_html__( 'Settings', 'forumax' ), esc_html__( 'Settings', 'forumax' ), $capability, 'forumax-settings', [ $this, 'forumax_settings_page' ] ); if ( ! $this->has_premium_analytics_page() ) { add_submenu_page( 'forumax', esc_html__( 'Analytics', 'forumax' ), esc_html__( 'Analytics', 'forumax' ), $capability, 'forumax-analytics', [ $this, 'forumax_analytics_page' ] ); } // --- SEPARATOR 3 (Before Account Section added by others) --- $this->add_frmx_separator( '3' ); } /** * Reorder the Forumax submenu to ensure Settings and others are correctly placed. */ public function reorder_forumax_submenu() { global $submenu; if ( ! isset( $submenu['forumax'] ) || ! is_array( $submenu['forumax'] ) ) { return; } $current_menu = $submenu['forumax']; $weights = [ 'forumax' => 1000, 'forumax-builder' => 2000, 'frmx-menu-sep-1' => 3000, ]; $weighted_items = []; foreach ( $current_menu as $key => $item ) { $slug = isset( $item[2] ) ? $item[2] : ''; $weight = 10000; // Default for "others" if ( isset( $weights[$slug] ) ) { $weight = $weights[$slug]; } elseif ( strpos( $slug, 'post_type=forum' ) !== false ) { $weight = 4000; if ( strpos( $slug, 'post-new.php' ) !== false ) $weight = 4100; elseif ( strpos( $slug, 'edit-tags.php' ) !== false ) $weight = 4200; } elseif ( $slug === 'frmx-menu-sep-after-forum' ) { $weight = 4900; } elseif ( strpos( $slug, 'post_type=topic' ) !== false ) { $weight = 5000; if ( strpos( $slug, 'post-new.php' ) !== false ) $weight = 5100; elseif ( strpos( $slug, 'edit-tags.php' ) !== false ) $weight = 5200; } elseif ( $slug === 'frmx-menu-sep-after-topic' ) { $weight = 5900; } elseif ( strpos( $slug, 'post_type=reply' ) !== false ) { $weight = 6000; if ( strpos( $slug, 'post-new.php' ) !== false ) $weight = 6100; elseif ( strpos( $slug, 'edit-tags.php' ) !== false ) $weight = 6200; } else { // Other knowns if ( $slug === 'frmx-menu-sep-2' ) $weight = 7000; elseif ( $slug === 'forumax-setup' ) $weight = 8000; elseif ( $slug === 'forumax-settings' ) $weight = 9000; // others default to 10000 elseif ( $slug === 'forumax-analytics' ) $weight = 11000; elseif ( $slug === 'frmx-menu-sep-3' ) $weight = 12000; } // Handle duplicate weights by incrementing while ( isset( $weighted_items[$weight] ) ) { $weight++; } $weighted_items[$weight] = $item; } ksort( $weighted_items ); $submenu['forumax'] = $weighted_items; } /** * Matches sidebar logic for parent menu expansion. */ public function fix_parent_active_state( $parent_file ) { global $current_screen; $bbp_types = [ 'forum', 'topic', 'reply' ]; if ( $current_screen && in_array( $current_screen->post_type, $bbp_types, true ) ) { return 'forumax'; } return $parent_file; } /** * Matches sidebar logic for specific submenu highlighting. */ public function fix_submenu_active_state( $submenu_file ) { global $current_screen, $pagenow; $bbp_types = [ 'forum', 'topic', 'reply' ]; $post_type = $current_screen ? $current_screen->post_type : ''; if ( in_array( $post_type, $bbp_types, true ) ) { if ( 'post-new.php' === $pagenow ) { return "post-new.php?post_type={$post_type}&is_child=true"; } if ( $current_screen->taxonomy ) { return "edit-tags.php?taxonomy={$current_screen->taxonomy}&post_type={$post_type}&is_child=true"; } return "edit.php?post_type={$post_type}"; } return $submenu_file; } /** * Helpers & Callbacks */ private function set_cpt_menu_parent( array $args ) : array { // Prevent WordPress from auto-generating a top-level or parented menu entry // for this CPT. Forumax adds explicit submenu links in forumax_admin_menu(). $args['show_in_menu'] = false; return $args; } private function add_frmx_separator( $id ) { add_submenu_page( 'forumax', '', '', 'manage_options', 'frmx-menu-sep-' . $id ); } public function forumax_plugin_page() { include plugin_dir_path( __FILE__ ) . '/menu/admin_ui.php'; } public function forumax_dashboard_page() { include plugin_dir_path( __FILE__ ) . '/menu/dashboard.php'; } public function forumax_setup_wizard_page() { include plugin_dir_path( __FILE__ ) . '/menu/setup-wizard.php'; } /** * AJAX handler — save all wizard settings. * * Accepts POST data from the setup wizard JS, validates input, * and persists the values as WordPress options. * * Flow: nonce → capability → sanitize → save → respond. */ public function ajax_save_wizard_settings() { if ( ! check_ajax_referer( 'forumax-wizard-nonce', 'nonce', false ) ) { wp_send_json_error( [ 'message' => __( 'Invalid security token.', 'forumax' ) ] ); } if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'message' => __( 'You do not have permission to save these settings.', 'forumax' ) ] ); } // ── Sanitize & save each field ────────────────────────────────── $post = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified above // Text fields: forum title + description. $text_fields = [ 'create_forum', 'forum_description' ]; foreach ( $text_fields as $field ) { if ( isset( $post[ $field ] ) ) { update_option( 'forumax_wizard_' . $field, 'forum_description' === $field ? sanitize_textarea_field( $post[ $field ] ) : sanitize_text_field( $post[ $field ] ), false ); } } // Boolean fields: auto-create pages. $bool_fields = [ 'create_pages' ]; foreach ( $bool_fields as $field ) { update_option( 'forumax_wizard_' . $field, isset( $post[ $field ] ) && '1' === $post[ $field ] ? '1' : '0', false ); } // Radio fields with allowlists. $allowed_setup_types = [ 'single', 'sample', 'skip' ]; $allowed_layout_styles = [ 'classic', 'modern', 'card' ]; if ( isset( $post['setup_type'] ) && in_array( $post['setup_type'], $allowed_setup_types, true ) ) { update_option( 'forumax_wizard_setup_type', sanitize_key( $post['setup_type'] ), false ); } if ( isset( $post['layout_style'] ) && in_array( $post['layout_style'], $allowed_layout_styles, true ) ) { update_option( 'forumax_wizard_layout_style', sanitize_key( $post['layout_style'] ), false ); } // Brand color — save to wizard option AND the real CSF settings for immediate effect. if ( ! empty( $post['brand_color'] ) ) { $brand_color = sanitize_hex_color( $post['brand_color'] ); if ( $brand_color ) { update_option( 'forumax_wizard_brand_color', $brand_color, false ); // Write directly to CSF option so the frontend picks it up immediately. $csf_options = get_option( 'bbp_core_settings', [] ); if ( ! is_array( $csf_options ) ) { $csf_options = []; } $csf_options['bbpc_brand_color'] = $brand_color; update_option( 'bbp_core_settings', $csf_options ); } } // Mark wizard as completed so it can be skipped on future visits. update_option( 'forumax_wizard_completed', '1', false ); wp_send_json_success( [ 'message' => __( 'Settings saved.', 'forumax' ) ] ); } public function forumax_settings_page() { if ( ! class_exists( '\CSF_Options' ) || ! class_exists( '\CSF' ) ) { wp_die( esc_html__( 'Forumax settings could not be loaded.', 'forumax' ) ); } if ( empty( \CSF::$args['admin_options']['bbp_core_settings'] ) || empty( \CSF::$args['sections']['bbp_core_settings'] ) ) { wp_die( esc_html__( 'Forumax settings are not registered.', 'forumax' ) ); } static $settings_renderer = null; if ( null === $settings_renderer ) { $settings_renderer = \CSF_Options::instance( 'bbp_core_settings', [ 'args' => \CSF::$args['admin_options']['bbp_core_settings'], 'sections' => \CSF::$args['sections']['bbp_core_settings'], ] ); } $settings_renderer->add_options_html(); } public function forumax_analytics_page() { include plugin_dir_path( __FILE__ ) . '/menu/analytics.php'; } private function has_premium_analytics_page() { return defined( 'BBPCPRO_PATH' ) || class_exists( '\Forumax_Pro\Admin\Analytics' ); } }