PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.4.3
Forumax – AI Powered Advanced Community Forum Plugin v2.4.3
2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 trunk 1.0.8 1.1.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 2.0.0 2.1.0 All 29 releases
bbp-core / includes / fse-compatibility.php

fse-compatibility.php in Forumax – AI Powered Advanced Community Forum Plugin 2.4.3, at includes/fse-compatibility.php

513 lines 14.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Full Site Editing (FSE) / Block Theme Compatibility for Forumax
4 *
5 * Fixes blank page issue when using block-based themes (e.g., Twenty Twenty-Five,
6 * Astra FSE, Kadence Blocks, etc.) with bbPress + Forumax.
7 *
8 * Root cause: bbPress tries to find a classic PHP theme template (page.php,
9 * index.php, etc.) to use as the wrapper for its content. Block themes do not
10 * ship classic PHP templates, so bbPress gets an empty string back from
11 * `bbp_get_theme_compat_templates()`, resulting in a completely blank page.
12 *
13 * Solution: Intercept the `bbp_template_include_theme_compat` filter and
14 * supply our own lightweight PHP wrapper template whenever no template was
15 * found and the active theme is a block/FSE theme.
16 *
17 * Edge Cases Handled:
18 * - Pure block themes (Twenty Twenty-Five, etc.)
19 * - Hybrid themes (theme.json + classic templates)
20 * - Child themes
21 * - Customizer preview mode
22 * - bbPress shortcodes on regular pages
23 * - REST API and AJAX requests
24 * - Multisite with different themes
25 *
26 * @package Forumax
27 * @since 2.3.1
28 */
29
30 defined( 'ABSPATH' ) || exit;
31
32 /**
33 * Detect if the currently active theme is a block/FSE theme.
34 *
35 * Handles edge cases:
36 * - Hybrid themes (has theme.json but also has classic templates)
37 * - Child themes that override parent behavior
38 * - Customizer preview with different theme
39 *
40 * @return bool True if block theme, false otherwise.
41 */
42 function forumax_is_block_theme() {
43 // Use static cache to avoid repeated checks.
44 static $is_block_theme = null;
45
46 if ( null !== $is_block_theme ) {
47 return $is_block_theme;
48 }
49
50 // Edge Case: Skip for REST API requests - they don't need template handling.
51 if ( forumax_is_rest_request() ) {
52 $is_block_theme = false;
53 return $is_block_theme;
54 }
55
56 // Edge Case: Skip for AJAX requests.
57 if ( wp_doing_ajax() ) {
58 $is_block_theme = false;
59 return $is_block_theme;
60 }
61
62 /**
63 * Filter to force block theme mode ON or OFF.
64 *
65 * Backward Compatibility: If you were using a hybrid theme with our
66 * FSE wrapper and want to keep using it, use this filter.
67 *
68 * @since 2.3.1
69 *
70 * @param bool|null $force_block_theme
71 * - true = Force treat as block theme (use our wrapper)
72 * - false = Force treat as classic theme (use theme's templates)
73 * - null = Auto-detect (default)
74 *
75 * Example - Force block theme mode for hybrid themes:
76 * add_filter( 'forumax_force_block_theme', '__return_true' );
77 *
78 * Example - Force classic theme mode:
79 * add_filter( 'forumax_force_block_theme', '__return_false' );
80 */
81 $force_block_theme = apply_filters( 'forumax_force_block_theme', null );
82
83 if ( true === $force_block_theme ) {
84 $is_block_theme = true;
85 return $is_block_theme;
86 }
87
88 if ( false === $force_block_theme ) {
89 $is_block_theme = false;
90 return $is_block_theme;
91 }
92
93 // Primary check: Use WordPress's built-in function (WP 5.9+).
94 if ( function_exists( 'wp_is_block_theme' ) ) {
95 $is_block_theme = (bool) wp_is_block_theme();
96
97 /**
98 * Filter to disable hybrid theme detection.
99 *
100 * Backward Compatibility: Previous versions treated all themes with
101 * theme.json as block themes. If this caused issues for your setup,
102 * you can disable hybrid detection to restore old behavior.
103 *
104 * @since 2.3.1
105 *
106 * @param bool $detect_hybrid Whether to detect hybrid themes. Default true.
107 */
108 $detect_hybrid = apply_filters( 'forumax_detect_hybrid_themes', true );
109
110 // Edge Case: Hybrid themes - has theme.json but also has index.php.
111 // If classic template exists, it's a hybrid theme - treat as classic.
112 if ( $is_block_theme && $detect_hybrid && forumax_theme_has_classic_templates() ) {
113 $is_block_theme = false;
114 }
115
116 return $is_block_theme;
117 }
118
119 // Fallback: check for theme.json in the theme root.
120 $has_theme_json = file_exists( trailingslashit( get_template_directory() ) . 'theme.json' );
121
122 // If theme.json exists, also check for classic templates.
123 $detect_hybrid = apply_filters( 'forumax_detect_hybrid_themes', true );
124 if ( $has_theme_json && $detect_hybrid && forumax_theme_has_classic_templates() ) {
125 $is_block_theme = false; // Hybrid theme.
126 } else {
127 $is_block_theme = $has_theme_json;
128 }
129
130 return $is_block_theme;
131 }
132
133 /**
134 * Check if theme has classic PHP templates.
135 *
136 * Used to detect hybrid themes that have both theme.json and classic templates.
137 *
138 * @return bool True if classic templates exist.
139 */
140 function forumax_theme_has_classic_templates() {
141 $template_dir = get_template_directory();
142 $stylesheet_dir = get_stylesheet_directory();
143
144 // Check for common classic templates in both parent and child theme.
145 $classic_templates = array( 'index.php', 'page.php', 'single.php', 'archive.php' );
146
147 foreach ( $classic_templates as $file ) {
148 // Check child theme first.
149 if ( $stylesheet_dir !== $template_dir && file_exists( trailingslashit( $stylesheet_dir ) . $file ) ) {
150 return true;
151 }
152 // Check parent theme.
153 if ( file_exists( trailingslashit( $template_dir ) . $file ) ) {
154 return true;
155 }
156 }
157
158 return false;
159 }
160
161 /**
162 * Check if current request is a REST API request.
163 *
164 * @return bool True if REST request.
165 */
166 function forumax_is_rest_request() {
167 if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
168 return true;
169 }
170
171 // Check URL path for /wp-json/.
172 if ( isset( $_SERVER['REQUEST_URI'] ) ) {
173 $rest_prefix = rest_get_url_prefix();
174 return strpos( $_SERVER['REQUEST_URI'], '/' . $rest_prefix . '/' ) !== false;
175 }
176
177 return false;
178 }
179
180 /**
181 * Check if we're in customizer preview mode.
182 *
183 * @return bool True if in customizer preview.
184 */
185 function forumax_is_customizer_preview() {
186 return is_customize_preview();
187 }
188
189 /**
190 * Provide a fallback PHP wrapper template for bbPress on block themes.
191 *
192 * bbPress hooks into `template_include` via `bbp_template_include_theme_compat`
193 * and returns the path of a classic PHP template (page.php, index.php …).
194 * On block themes no such file exists, so bbPress returns an empty/invalid
195 * template path → blank page.
196 *
197 * This filter runs AFTER bbPress's own filter (priority 10, we use 20) and
198 * replaces an empty or missing template with our lightweight wrapper.
199 *
200 * @param string $template Path returned by bbPress.
201 * @return string Path to a valid PHP template file.
202 */
203 add_filter( 'bbp_template_include_theme_compat', 'forumax_fse_fallback_template', 20 );
204 function forumax_fse_fallback_template( $template ) {
205
206 // Only act when a block theme is active.
207 if ( ! forumax_is_block_theme() ) {
208 return $template;
209 }
210
211 // Only act on actual bbPress pages.
212 if ( ! function_exists( 'is_bbpress' ) || ! is_bbpress() ) {
213 return $template;
214 }
215
216 // Edge Case: Customizer preview - let WordPress handle it.
217 if ( forumax_is_customizer_preview() ) {
218 return $template;
219 }
220
221 // If bbPress resolved a bbPress-specific template, leave it alone.
222 if ( ! empty( $template ) && file_exists( $template ) ) {
223 $bbpress_specific = array( 'plugin-bbpress.php', 'bbpress.php', 'forums.php', 'forum.php' );
224 if ( in_array( basename( $template ), $bbpress_specific, true ) ) {
225 return $template;
226 }
227 }
228
229 // Edge Case: Check if theme provides its own bbPress template.
230 $theme_bbpress_template = locate_template( array( 'bbpress.php', 'bbpress/bbpress.php' ) );
231 if ( ! empty( $theme_bbpress_template ) ) {
232 return $theme_bbpress_template;
233 }
234
235 // Our bundled PHP wrapper template that works with any theme.
236 $wrapper = FORUMAX_PATH . 'templates/bbpress-wrapper.php';
237
238 if ( file_exists( $wrapper ) ) {
239 return $wrapper;
240 }
241
242 return $template;
243 }
244
245 /**
246 * Ultimate fallback: Hook into template_include as last resort.
247 *
248 * If bbPress filter didn't catch it, this will.
249 * Priority 9999 ensures we run after almost everything else.
250 *
251 * @param string $template Current template path.
252 * @return string Template path.
253 */
254 add_filter( 'template_include', 'forumax_fse_ultimate_fallback', 9999 );
255 function forumax_fse_ultimate_fallback( $template ) {
256 // Only act when a block theme is active.
257 if ( ! forumax_is_block_theme() ) {
258 return $template;
259 }
260
261 // Only act on bbPress pages.
262 if ( ! function_exists( 'is_bbpress' ) || ! is_bbpress() ) {
263 return $template;
264 }
265
266 // Edge Case: Customizer preview - don't interfere.
267 if ( forumax_is_customizer_preview() ) {
268 return $template;
269 }
270
271 // If we have a valid template already, don't interfere.
272 if ( ! empty( $template ) && file_exists( $template ) && filesize( $template ) > 0 ) {
273 return $template;
274 }
275
276 // Use our wrapper as last resort.
277 $wrapper = FORUMAX_PATH . 'templates/bbpress-wrapper.php';
278
279 if ( file_exists( $wrapper ) ) {
280 return $wrapper;
281 }
282
283 return $template;
284 }
285
286 /**
287 * Handle bbPress shortcode on regular pages in block themes.
288 *
289 * Edge Case: When bbPress shortcode [bbp-forum-index] is used on a regular
290 * page (not bbPress archive), we need to ensure styles are loaded.
291 */
292 add_action( 'wp', 'forumax_handle_bbpress_shortcode_pages' );
293 function forumax_handle_bbpress_shortcode_pages() {
294 if ( ! forumax_is_block_theme() ) {
295 return;
296 }
297
298 // Check if current page/post has bbPress shortcodes.
299 if ( ! is_singular() ) {
300 return;
301 }
302
303 $post = get_post();
304 if ( ! $post ) {
305 return;
306 }
307
308 // List of bbPress shortcodes.
309 $bbpress_shortcodes = array(
310 'bbp-forum-index',
311 'bbp-forum-form',
312 'bbp-single-forum',
313 'bbp-topic-index',
314 'bbp-topic-form',
315 'bbp-single-topic',
316 'bbp-reply-form',
317 'bbp-single-reply',
318 'bbp-single-view',
319 'bbp-search-form',
320 'bbp-search',
321 'bbp-login',
322 'bbp-register',
323 'bbp-lost-pass',
324 );
325
326 $has_bbpress_shortcode = false;
327 foreach ( $bbpress_shortcodes as $shortcode ) {
328 if ( has_shortcode( $post->post_content, $shortcode ) ) {
329 $has_bbpress_shortcode = true;
330 break;
331 }
332 }
333
334 // Also check for bbPress blocks.
335 if ( ! $has_bbpress_shortcode && function_exists( 'has_block' ) ) {
336 $bbpress_blocks = array( 'bbpress/forum-index', 'bbpress/topic-index', 'forumax/forums' );
337 foreach ( $bbpress_blocks as $block ) {
338 if ( has_block( $block, $post ) ) {
339 $has_bbpress_shortcode = true;
340 break;
341 }
342 }
343 }
344
345 if ( $has_bbpress_shortcode ) {
346 // Add body class for styling.
347 add_filter( 'body_class', function( $classes ) {
348 $classes[] = 'forumax-shortcode-page';
349 $classes[] = 'forumax-fse';
350 return $classes;
351 } );
352
353 // Ensure bbPress styles are loaded.
354 add_action( 'wp_enqueue_scripts', 'forumax_fse_ensure_assets', 100 );
355 }
356 }
357
358 /**
359 * Add bbPress body classes even on block themes.
360 *
361 * @param array $classes Existing body classes.
362 * @return array Modified body classes.
363 */
364 add_filter( 'body_class', 'forumax_add_bbpress_body_classes_on_fse', 20 );
365 function forumax_add_bbpress_body_classes_on_fse( $classes ) {
366 if ( ! forumax_is_block_theme() ) {
367 return $classes;
368 }
369
370 if ( ! function_exists( 'is_bbpress' ) || ! is_bbpress() ) {
371 return $classes;
372 }
373
374 if ( ! in_array( 'bbpress', $classes, true ) ) {
375 $classes[] = 'bbpress';
376 }
377
378 if ( ! in_array( 'forumax-fse', $classes, true ) ) {
379 $classes[] = 'forumax-fse';
380 }
381
382 // Edge Case: Add specific page type classes for better styling control.
383 if ( function_exists( 'bbp_is_single_forum' ) && bbp_is_single_forum() ) {
384 $classes[] = 'forumax-single-forum';
385 } elseif ( function_exists( 'bbp_is_single_topic' ) && bbp_is_single_topic() ) {
386 $classes[] = 'forumax-single-topic';
387 } elseif ( function_exists( 'bbp_is_forum_archive' ) && bbp_is_forum_archive() ) {
388 $classes[] = 'forumax-forum-archive';
389 } elseif ( function_exists( 'bbp_is_topic_archive' ) && bbp_is_topic_archive() ) {
390 $classes[] = 'forumax-topic-archive';
391 } elseif ( function_exists( 'bbp_is_single_user' ) && bbp_is_single_user() ) {
392 $classes[] = 'forumax-user-profile';
393 } elseif ( function_exists( 'bbp_is_search' ) && bbp_is_search() ) {
394 $classes[] = 'forumax-search';
395 }
396
397 return $classes;
398 }
399
400 /**
401 * Add critical inline CSS for FSE themes.
402 *
403 * Ensures basic layout works even if external CSS fails to load.
404 */
405 add_action( 'wp_head', 'forumax_fse_critical_css', 5 );
406 function forumax_fse_critical_css() {
407 if ( ! forumax_is_block_theme() ) {
408 return;
409 }
410
411 // Check for bbPress page OR shortcode page.
412 $is_bbpress_page = function_exists( 'is_bbpress' ) && is_bbpress();
413 $is_shortcode_page = in_array( 'forumax-shortcode-page', get_body_class(), true );
414
415 if ( ! $is_bbpress_page && ! $is_shortcode_page ) {
416 return;
417 }
418
419 ?>
420 <style id="forumax-fse-critical">
421 /* Forumax FSE Critical CSS - Ensures forum displays correctly in block themes */
422 .forumax-fse #frmx-bbpress-main,
423 .forumax-fse .entry-content {
424 padding: 40px 20px;
425 max-width: 1200px;
426 margin: 0 auto;
427 width: 100%;
428 box-sizing: border-box;
429 }
430 .forumax-fse .frmx-row {
431 display: flex;
432 flex-wrap: wrap;
433 gap: 0;
434 margin-right: -15px;
435 margin-left: -15px;
436 align-items: flex-start;
437 }
438 .forumax-fse .frmx-row > [class*="frmx-col"] {
439 flex-shrink: 0;
440 min-width: 0;
441 box-sizing: border-box;
442 padding-right: 15px;
443 padding-left: 15px;
444 }
445 .forumax-fse .frmx-col-lg-8 {
446 flex: 1 1 0%;
447 }
448 .forumax-fse .frmx-col-lg-4 {
449 flex: 0 0 280px;
450 max-width: 280px;
451 width: 280px;
452 }
453 /* Edge Case: Shortcode pages need full width */
454 .forumax-shortcode-page .bbp-forum-content,
455 .forumax-shortcode-page .bbp-topic-content {
456 width: 100%;
457 }
458 @media (max-width: 768px) {
459 .forumax-fse .frmx-row {
460 flex-direction: column;
461 }
462 .forumax-fse .frmx-col-lg-4 {
463 flex: 0 0 100%;
464 max-width: 100%;
465 width: 100%;
466 }
467 }
468 </style>
469 <?php
470 }
471
472 /**
473 * Ensure bbPress scripts and styles load on FSE themes.
474 *
475 * Some FSE themes may interfere with enqueueing.
476 */
477 add_action( 'wp_enqueue_scripts', 'forumax_fse_ensure_assets', 100 );
478 function forumax_fse_ensure_assets() {
479 if ( ! forumax_is_block_theme() ) {
480 return;
481 }
482
483 // Check for bbPress page OR shortcode page.
484 $is_bbpress_page = function_exists( 'is_bbpress' ) && is_bbpress();
485
486 if ( ! $is_bbpress_page ) {
487 return;
488 }
489
490 // Ensure bbPress default styles are loaded.
491 if ( function_exists( 'bbp_default_styles' ) && ! wp_style_is( 'bbp-default', 'enqueued' ) ) {
492 bbp_default_styles();
493 }
494 }
495
496 /**
497 * Filter to allow developers to disable FSE compatibility.
498 *
499 * Usage: add_filter( 'forumax_disable_fse_compatibility', '__return_true' );
500 *
501 * @since 2.3.1
502 */
503 add_action( 'plugins_loaded', 'forumax_maybe_disable_fse_compatibility', 5 );
504 function forumax_maybe_disable_fse_compatibility() {
505 if ( apply_filters( 'forumax_disable_fse_compatibility', false ) ) {
506 remove_filter( 'bbp_template_include_theme_compat', 'forumax_fse_fallback_template', 20 );
507 remove_filter( 'template_include', 'forumax_fse_ultimate_fallback', 9999 );
508 remove_filter( 'body_class', 'forumax_add_bbpress_body_classes_on_fse', 20 );
509 remove_action( 'wp_head', 'forumax_fse_critical_css', 5 );
510 remove_action( 'wp_enqueue_scripts', 'forumax_fse_ensure_assets', 100 );
511 }
512 }
513