# bbp-core/trunk/includes/admin/Menu.php

Forumax – AI Powered Advanced Community Forum Plugin, version trunk. 424 lines.

- Page: https://pluginprobe.com/plugins/bbp-core/trunk/code/includes/admin/Menu.php
- Raw: https://pluginprobe.com/plugins/bbp-core/trunk/raw/includes/admin/Menu.php
- Modified: 2026-06-10T13:02:40+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/bbp-core/trunk/code/includes/admin/Menu.php#L10-L20`.

```php
<?php

namespace admin;

/**
 * Handle Forumax Admin Menu logic.
 */
class Menu {

	public function __construct() {
		add_filter( 'bbp_register_forum_post_type', [ $this, 'hide_bbp_cpt_top_level_menu' ] );
		add_filter( 'bbp_register_topic_post_type', [ $this, 'hide_bbp_cpt_top_level_menu' ] );
		add_filter( 'bbp_register_reply_post_type', [ $this, 'hide_bbp_cpt_top_level_menu' ] );

		// Use priority 99 to ensure we run after other plugins (like bbPress) to hide their menus if needed.
		add_action( 'admin_menu', [ $this, 'forumax_admin_menu' ], 99 );
		add_action( 'admin_menu', [ $this, 'reorder_forumax_submenu' ], 999 );
		add_filter( 'parent_file', [ $this, 'fix_parent_active_state' ] );
		add_filter( 'submenu_file', [ $this, 'fix_submenu_active_state' ] );

		// Enqueue admin assets specifically for our menu styling
		add_filter( 'admin_body_class', [ $this, 'add_admin_body_class' ] );

		// Setup wizard AJAX save handler
		add_action( 'wp_ajax_forumax_save_wizard_settings', [ $this, 'ajax_save_wizard_settings' ] );
	}

	/**
	 * Add a specific class to the admin body to target our sidebar styling reliably.
	 */
	public function add_admin_body_class( $classes ) {
		$screen = get_current_screen();
		if ( $screen && ( 'forumax' === $screen->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,
					'&nbsp;&nbsp;&nbsp; ' . 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 ),
						'&nbsp;&nbsp;&nbsp; ' . 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',
			'',
			'<span class="frmx-menu-separator" aria-hidden="true"></span>',
			'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' );
	}
}

```
