# bbp-core/trunk/includes/fse-compatibility.php

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

- Page: https://pluginprobe.com/plugins/bbp-core/trunk/code/includes/fse-compatibility.php
- Raw: https://pluginprobe.com/plugins/bbp-core/trunk/raw/includes/fse-compatibility.php
- Modified: 2026-04-10T15:32:42+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/fse-compatibility.php#L10-L20`.

```php
<?php
/**
 * Full Site Editing (FSE) / Block Theme Compatibility for Forumax
 *
 * Fixes blank page issue when using block-based themes (e.g., Twenty Twenty-Five,
 * Astra FSE, Kadence Blocks, etc.) with bbPress + Forumax.
 *
 * Root cause: bbPress tries to find a classic PHP theme template (page.php,
 * index.php, etc.) to use as the wrapper for its content. Block themes do not
 * ship classic PHP templates, so bbPress gets an empty string back from
 * `bbp_get_theme_compat_templates()`, resulting in a completely blank page.
 *
 * Solution: Intercept the `bbp_template_include_theme_compat` filter and
 * supply our own lightweight PHP wrapper template whenever no template was
 * found and the active theme is a block/FSE theme.
 *
 * Edge Cases Handled:
 * - Pure block themes (Twenty Twenty-Five, etc.)
 * - Hybrid themes (theme.json + classic templates)
 * - Child themes
 * - Customizer preview mode
 * - bbPress shortcodes on regular pages
 * - REST API and AJAX requests
 * - Multisite with different themes
 *
 * @package Forumax
 * @since   2.3.1
 */

defined( 'ABSPATH' ) || exit;

/**
 * Detect if the currently active theme is a block/FSE theme.
 *
 * Handles edge cases:
 * - Hybrid themes (has theme.json but also has classic templates)
 * - Child themes that override parent behavior
 * - Customizer preview with different theme
 *
 * @return bool True if block theme, false otherwise.
 */
function forumax_is_block_theme() {
	// Use static cache to avoid repeated checks.
	static $is_block_theme = null;

	if ( null !== $is_block_theme ) {
		return $is_block_theme;
	}

	// Edge Case: Skip for REST API requests - they don't need template handling.
	if ( forumax_is_rest_request() ) {
		$is_block_theme = false;
		return $is_block_theme;
	}

	// Edge Case: Skip for AJAX requests.
	if ( wp_doing_ajax() ) {
		$is_block_theme = false;
		return $is_block_theme;
	}

	/**
	 * Filter to force block theme mode ON or OFF.
	 *
	 * Backward Compatibility: If you were using a hybrid theme with our
	 * FSE wrapper and want to keep using it, use this filter.
	 *
	 * @since 2.3.1
	 *
	 * @param bool|null $force_block_theme
	 *   - true  = Force treat as block theme (use our wrapper)
	 *   - false = Force treat as classic theme (use theme's templates)
	 *   - null  = Auto-detect (default)
	 *
	 * Example - Force block theme mode for hybrid themes:
	 * add_filter( 'forumax_force_block_theme', '__return_true' );
	 *
	 * Example - Force classic theme mode:
	 * add_filter( 'forumax_force_block_theme', '__return_false' );
	 */
	$force_block_theme = apply_filters( 'forumax_force_block_theme', null );

	if ( true === $force_block_theme ) {
		$is_block_theme = true;
		return $is_block_theme;
	}

	if ( false === $force_block_theme ) {
		$is_block_theme = false;
		return $is_block_theme;
	}

	// Primary check: Use WordPress's built-in function (WP 5.9+).
	if ( function_exists( 'wp_is_block_theme' ) ) {
		$is_block_theme = (bool) wp_is_block_theme();

		/**
		 * Filter to disable hybrid theme detection.
		 *
		 * Backward Compatibility: Previous versions treated all themes with
		 * theme.json as block themes. If this caused issues for your setup,
		 * you can disable hybrid detection to restore old behavior.
		 *
		 * @since 2.3.1
		 *
		 * @param bool $detect_hybrid Whether to detect hybrid themes. Default true.
		 */
		$detect_hybrid = apply_filters( 'forumax_detect_hybrid_themes', true );

		// Edge Case: Hybrid themes - has theme.json but also has index.php.
		// If classic template exists, it's a hybrid theme - treat as classic.
		if ( $is_block_theme && $detect_hybrid && forumax_theme_has_classic_templates() ) {
			$is_block_theme = false;
		}

		return $is_block_theme;
	}

	// Fallback: check for theme.json in the theme root.
	$has_theme_json = file_exists( trailingslashit( get_template_directory() ) . 'theme.json' );

	// If theme.json exists, also check for classic templates.
	$detect_hybrid = apply_filters( 'forumax_detect_hybrid_themes', true );
	if ( $has_theme_json && $detect_hybrid && forumax_theme_has_classic_templates() ) {
		$is_block_theme = false; // Hybrid theme.
	} else {
		$is_block_theme = $has_theme_json;
	}

	return $is_block_theme;
}

/**
 * Check if theme has classic PHP templates.
 *
 * Used to detect hybrid themes that have both theme.json and classic templates.
 *
 * @return bool True if classic templates exist.
 */
function forumax_theme_has_classic_templates() {
	$template_dir = get_template_directory();
	$stylesheet_dir = get_stylesheet_directory();

	// Check for common classic templates in both parent and child theme.
	$classic_templates = array( 'index.php', 'page.php', 'single.php', 'archive.php' );

	foreach ( $classic_templates as $file ) {
		// Check child theme first.
		if ( $stylesheet_dir !== $template_dir && file_exists( trailingslashit( $stylesheet_dir ) . $file ) ) {
			return true;
		}
		// Check parent theme.
		if ( file_exists( trailingslashit( $template_dir ) . $file ) ) {
			return true;
		}
	}

	return false;
}

/**
 * Check if current request is a REST API request.
 *
 * @return bool True if REST request.
 */
function forumax_is_rest_request() {
	if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
		return true;
	}

	// Check URL path for /wp-json/.
	if ( isset( $_SERVER['REQUEST_URI'] ) ) {
		$rest_prefix = rest_get_url_prefix();
		return strpos( $_SERVER['REQUEST_URI'], '/' . $rest_prefix . '/' ) !== false;
	}

	return false;
}

/**
 * Check if we're in customizer preview mode.
 *
 * @return bool True if in customizer preview.
 */
function forumax_is_customizer_preview() {
	return is_customize_preview();
}

/**
 * Provide a fallback PHP wrapper template for bbPress on block themes.
 *
 * bbPress hooks into `template_include` via `bbp_template_include_theme_compat`
 * and returns the path of a classic PHP template (page.php, index.php …).
 * On block themes no such file exists, so bbPress returns an empty/invalid
 * template path → blank page.
 *
 * This filter runs AFTER bbPress's own filter (priority 10, we use 20) and
 * replaces an empty or missing template with our lightweight wrapper.
 *
 * @param  string $template Path returned by bbPress.
 * @return string           Path to a valid PHP template file.
 */
add_filter( 'bbp_template_include_theme_compat', 'forumax_fse_fallback_template', 20 );
function forumax_fse_fallback_template( $template ) {

	// Only act when a block theme is active.
	if ( ! forumax_is_block_theme() ) {
		return $template;
	}

	// Only act on actual bbPress pages.
	if ( ! function_exists( 'is_bbpress' ) || ! is_bbpress() ) {
		return $template;
	}

	// Edge Case: Customizer preview - let WordPress handle it.
	if ( forumax_is_customizer_preview() ) {
		return $template;
	}

	// If bbPress resolved a bbPress-specific template, leave it alone.
	if ( ! empty( $template ) && file_exists( $template ) ) {
		$bbpress_specific = array( 'plugin-bbpress.php', 'bbpress.php', 'forums.php', 'forum.php' );
		if ( in_array( basename( $template ), $bbpress_specific, true ) ) {
			return $template;
		}
	}

	// Edge Case: Check if theme provides its own bbPress template.
	$theme_bbpress_template = locate_template( array( 'bbpress.php', 'bbpress/bbpress.php' ) );
	if ( ! empty( $theme_bbpress_template ) ) {
		return $theme_bbpress_template;
	}

	// Our bundled PHP wrapper template that works with any theme.
	$wrapper = FORUMAX_PATH . 'templates/bbpress-wrapper.php';

	if ( file_exists( $wrapper ) ) {
		return $wrapper;
	}

	return $template;
}

/**
 * Ultimate fallback: Hook into template_include as last resort.
 *
 * If bbPress filter didn't catch it, this will.
 * Priority 9999 ensures we run after almost everything else.
 *
 * @param string $template Current template path.
 * @return string Template path.
 */
add_filter( 'template_include', 'forumax_fse_ultimate_fallback', 9999 );
function forumax_fse_ultimate_fallback( $template ) {
	// Only act when a block theme is active.
	if ( ! forumax_is_block_theme() ) {
		return $template;
	}

	// Only act on bbPress pages.
	if ( ! function_exists( 'is_bbpress' ) || ! is_bbpress() ) {
		return $template;
	}

	// Edge Case: Customizer preview - don't interfere.
	if ( forumax_is_customizer_preview() ) {
		return $template;
	}

	// If we have a valid template already, don't interfere.
	if ( ! empty( $template ) && file_exists( $template ) && filesize( $template ) > 0 ) {
		return $template;
	}

	// Use our wrapper as last resort.
	$wrapper = FORUMAX_PATH . 'templates/bbpress-wrapper.php';

	if ( file_exists( $wrapper ) ) {
		return $wrapper;
	}

	return $template;
}

/**
 * Handle bbPress shortcode on regular pages in block themes.
 *
 * Edge Case: When bbPress shortcode [bbp-forum-index] is used on a regular
 * page (not bbPress archive), we need to ensure styles are loaded.
 */
add_action( 'wp', 'forumax_handle_bbpress_shortcode_pages' );
function forumax_handle_bbpress_shortcode_pages() {
	if ( ! forumax_is_block_theme() ) {
		return;
	}

	// Check if current page/post has bbPress shortcodes.
	if ( ! is_singular() ) {
		return;
	}

	$post = get_post();
	if ( ! $post ) {
		return;
	}

	// List of bbPress shortcodes.
	$bbpress_shortcodes = array(
		'bbp-forum-index',
		'bbp-forum-form',
		'bbp-single-forum',
		'bbp-topic-index',
		'bbp-topic-form',
		'bbp-single-topic',
		'bbp-reply-form',
		'bbp-single-reply',
		'bbp-single-view',
		'bbp-search-form',
		'bbp-search',
		'bbp-login',
		'bbp-register',
		'bbp-lost-pass',
	);

	$has_bbpress_shortcode = false;
	foreach ( $bbpress_shortcodes as $shortcode ) {
		if ( has_shortcode( $post->post_content, $shortcode ) ) {
			$has_bbpress_shortcode = true;
			break;
		}
	}

	// Also check for bbPress blocks.
	if ( ! $has_bbpress_shortcode && function_exists( 'has_block' ) ) {
		$bbpress_blocks = array( 'bbpress/forum-index', 'bbpress/topic-index', 'forumax/forums' );
		foreach ( $bbpress_blocks as $block ) {
			if ( has_block( $block, $post ) ) {
				$has_bbpress_shortcode = true;
				break;
			}
		}
	}

	if ( $has_bbpress_shortcode ) {
		// Add body class for styling.
		add_filter( 'body_class', function( $classes ) {
			$classes[] = 'forumax-shortcode-page';
			$classes[] = 'forumax-fse';
			return $classes;
		} );

		// Ensure bbPress styles are loaded.
		add_action( 'wp_enqueue_scripts', 'forumax_fse_ensure_assets', 100 );
	}
}

/**
 * Add bbPress body classes even on block themes.
 *
 * @param array $classes Existing body classes.
 * @return array Modified body classes.
 */
add_filter( 'body_class', 'forumax_add_bbpress_body_classes_on_fse', 20 );
function forumax_add_bbpress_body_classes_on_fse( $classes ) {
	if ( ! forumax_is_block_theme() ) {
		return $classes;
	}

	if ( ! function_exists( 'is_bbpress' ) || ! is_bbpress() ) {
		return $classes;
	}

	if ( ! in_array( 'bbpress', $classes, true ) ) {
		$classes[] = 'bbpress';
	}

	if ( ! in_array( 'forumax-fse', $classes, true ) ) {
		$classes[] = 'forumax-fse';
	}

	// Edge Case: Add specific page type classes for better styling control.
	if ( function_exists( 'bbp_is_single_forum' ) && bbp_is_single_forum() ) {
		$classes[] = 'forumax-single-forum';
	} elseif ( function_exists( 'bbp_is_single_topic' ) && bbp_is_single_topic() ) {
		$classes[] = 'forumax-single-topic';
	} elseif ( function_exists( 'bbp_is_forum_archive' ) && bbp_is_forum_archive() ) {
		$classes[] = 'forumax-forum-archive';
	} elseif ( function_exists( 'bbp_is_topic_archive' ) && bbp_is_topic_archive() ) {
		$classes[] = 'forumax-topic-archive';
	} elseif ( function_exists( 'bbp_is_single_user' ) && bbp_is_single_user() ) {
		$classes[] = 'forumax-user-profile';
	} elseif ( function_exists( 'bbp_is_search' ) && bbp_is_search() ) {
		$classes[] = 'forumax-search';
	}

	return $classes;
}

/**
 * Add critical inline CSS for FSE themes.
 *
 * Ensures basic layout works even if external CSS fails to load.
 */
add_action( 'wp_head', 'forumax_fse_critical_css', 5 );
function forumax_fse_critical_css() {
	if ( ! forumax_is_block_theme() ) {
		return;
	}

	// Check for bbPress page OR shortcode page.
	$is_bbpress_page = function_exists( 'is_bbpress' ) && is_bbpress();
	$is_shortcode_page = in_array( 'forumax-shortcode-page', get_body_class(), true );

	if ( ! $is_bbpress_page && ! $is_shortcode_page ) {
		return;
	}

	?>
	<style id="forumax-fse-critical">
	/* Forumax FSE Critical CSS - Ensures forum displays correctly in block themes */
	.forumax-fse #frmx-bbpress-main,
	.forumax-fse .entry-content {
		padding: 40px 20px;
		max-width: 1200px;
		margin: 0 auto;
		width: 100%;
		box-sizing: border-box;
	}
	.forumax-fse .frmx-row {
		display: flex;
		flex-wrap: wrap;
		gap: 0;
		margin-right: -15px;
		margin-left: -15px;
		align-items: flex-start;
	}
	.forumax-fse .frmx-row > [class*="frmx-col"] {
		flex-shrink: 0;
		min-width: 0;
		box-sizing: border-box;
		padding-right: 15px;
		padding-left: 15px;
	}
	.forumax-fse .frmx-col-lg-8 {
		flex: 1 1 0%;
	}
	.forumax-fse .frmx-col-lg-4 {
		flex: 0 0 280px;
		max-width: 280px;
		width: 280px;
	}
	/* Edge Case: Shortcode pages need full width */
	.forumax-shortcode-page .bbp-forum-content,
	.forumax-shortcode-page .bbp-topic-content {
		width: 100%;
	}
	@media (max-width: 768px) {
		.forumax-fse .frmx-row {
			flex-direction: column;
		}
		.forumax-fse .frmx-col-lg-4 {
			flex: 0 0 100%;
			max-width: 100%;
			width: 100%;
		}
	}
	</style>
	<?php
}

/**
 * Ensure bbPress scripts and styles load on FSE themes.
 *
 * Some FSE themes may interfere with enqueueing.
 */
add_action( 'wp_enqueue_scripts', 'forumax_fse_ensure_assets', 100 );
function forumax_fse_ensure_assets() {
	if ( ! forumax_is_block_theme() ) {
		return;
	}

	// Check for bbPress page OR shortcode page.
	$is_bbpress_page = function_exists( 'is_bbpress' ) && is_bbpress();

	if ( ! $is_bbpress_page ) {
		return;
	}

	// Ensure bbPress default styles are loaded.
	if ( function_exists( 'bbp_default_styles' ) && ! wp_style_is( 'bbp-default', 'enqueued' ) ) {
		bbp_default_styles();
	}
}

/**
 * Filter to allow developers to disable FSE compatibility.
 *
 * Usage: add_filter( 'forumax_disable_fse_compatibility', '__return_true' );
 *
 * @since 2.3.1
 */
add_action( 'plugins_loaded', 'forumax_maybe_disable_fse_compatibility', 5 );
function forumax_maybe_disable_fse_compatibility() {
	if ( apply_filters( 'forumax_disable_fse_compatibility', false ) ) {
		remove_filter( 'bbp_template_include_theme_compat', 'forumax_fse_fallback_template', 20 );
		remove_filter( 'template_include', 'forumax_fse_ultimate_fallback', 9999 );
		remove_filter( 'body_class', 'forumax_add_bbpress_body_classes_on_fse', 20 );
		remove_action( 'wp_head', 'forumax_fse_critical_css', 5 );
		remove_action( 'wp_enqueue_scripts', 'forumax_fse_ensure_assets', 100 );
	}
}

```
