| @@ -300,21 +300,116 @@ | ||
| 300 | 300 | return $empty + $menu_locations; |
| 301 | 301 | } |
| 302 | 302 | |
| 303 | 303 | /** |
| 304 | - * Enable REST API for bbPress forum post type | |
| 305 | - * This allows the forum post type to be accessed via the WordPress REST API | |
| 306 | - * which is required for the Gutenberg block to fetch forum data | |
| 304 | + * Fix Gutenberg editor support for bbPress forum and topic post types. | |
| 305 | + * | |
| 306 | + * Filters the post type registration arguments to enable full REST API | |
| 307 | + * support, ensure thumbnail support, and force correct labels so that | |
| 308 | + * Gutenberg displays the proper post type name instead of "Document". | |
| 309 | + * | |
| 310 | + * @param array $args Array of arguments for registering a post type. | |
| 311 | + * @param string $post_type Post type key. | |
| 312 | + * @return array Modified arguments. | |
| 307 | 313 | */ |
| 308 | -function forumax_enable_forum_rest_api() { | |
| 309 | - $post_type_obj = get_post_type_object( 'forum' ); | |
| 310 | - if ( $post_type_obj ) { | |
| 311 | - $post_type_obj->show_in_rest = true; | |
| 314 | +function forumax_fix_post_type_args( $args, $post_type ) { | |
| 315 | + $post_types_config = array( | |
| 316 | + 'forum' => array( | |
| 317 | + 'rest_base' => 'forums', | |
| 318 | + 'labels' => array( | |
| 319 | + 'name' => __( 'Forums', 'bbpress' ), | |
| 320 | + 'singular_name' => __( 'Forum', 'bbpress' ), | |
| 321 | + ), | |
| 322 | + ), | |
| 323 | + 'topic' => array( | |
| 324 | + 'rest_base' => 'topics', | |
| 325 | + 'labels' => array( | |
| 326 | + 'name' => __( 'Topics', 'bbpress' ), | |
| 327 | + 'singular_name' => __( 'Topic', 'bbpress' ), | |
| 328 | + ), | |
| 329 | + ), | |
| 330 | + ); | |
| 331 | + | |
| 332 | + if ( ! isset( $post_types_config[ $post_type ] ) ) { | |
| 333 | + return $args; | |
| 312 | 334 | } |
| 335 | + | |
| 336 | + $config = $post_types_config[ $post_type ]; | |
| 337 | + | |
| 338 | + // Force full REST API support for Gutenberg. | |
| 339 | + $args['show_in_rest'] = true; | |
| 340 | + $args['rest_base'] = $config['rest_base']; | |
| 341 | + $args['rest_controller_class'] = 'WP_REST_Posts_Controller'; | |
| 342 | + | |
| 343 | + // Set explicit label property (singular string). | |
| 344 | + $args['label'] = $config['labels']['name']; | |
| 345 | + | |
| 346 | + // Ensure thumbnail support is included. | |
| 347 | + if ( ! empty( $args['supports'] ) && is_array( $args['supports'] ) ) { | |
| 348 | + if ( ! in_array( 'thumbnail', $args['supports'], true ) ) { | |
| 349 | + $args['supports'][] = 'thumbnail'; | |
| 350 | + } | |
| 351 | + } else { | |
| 352 | + $args['supports'] = array( 'title', 'editor', 'revisions', 'thumbnail' ); | |
| 353 | + } | |
| 354 | + | |
| 355 | + // Merge labels to prevent Gutenberg "Document" fallback. | |
| 356 | + if ( ! empty( $args['labels'] ) && is_array( $args['labels'] ) ) { | |
| 357 | + $args['labels'] = array_merge( $args['labels'], $config['labels'] ); | |
| 358 | + } else { | |
| 359 | + $args['labels'] = $config['labels']; | |
| 360 | + } | |
| 361 | + | |
| 362 | + return $args; | |
| 313 | 363 | } |
| 314 | -add_action( 'init', 'forumax_enable_forum_rest_api', 20 ); | |
| 364 | +add_filter( 'register_post_type_args', 'forumax_fix_post_type_args', 99, 2 ); | |
| 315 | 365 | |
| 316 | 366 | /** |
| 367 | + * Grant bbPress post type capabilities to WordPress administrators. | |
| 368 | + * | |
| 369 | + * Gutenberg requests the post type REST endpoint with context=edit, which | |
| 370 | + * checks custom capabilities like edit_forums and edit_topics. WordPress | |
| 371 | + * administrators may not have these caps unless bbPress has explicitly | |
| 372 | + * assigned them. This filter dynamically grants the required caps to | |
| 373 | + * any user who can manage_options (i.e. administrators). | |
| 374 | + * | |
| 375 | + * @param array $allcaps All capabilities for the user. | |
| 376 | + * @param array $caps Required capabilities being checked. | |
| 377 | + * @param array $args Additional arguments passed to the check. | |
| 378 | + * @return array Modified capabilities array. | |
| 379 | + */ | |
| 380 | +function forumax_grant_bbpress_caps_to_admins( $allcaps, $caps, $args ) { | |
| 381 | + // Only grant to users who can manage options (administrators). | |
| 382 | + if ( empty( $allcaps['manage_options'] ) ) { | |
| 383 | + return $allcaps; | |
| 384 | + } | |
| 385 | + | |
| 386 | + // bbPress forum and topic capabilities needed for REST API access. | |
| 387 | + $bbpress_caps = array( | |
| 388 | + 'edit_forums', | |
| 389 | + 'edit_others_forums', | |
| 390 | + 'publish_forums', | |
| 391 | + 'read_private_forums', | |
| 392 | + 'read_hidden_forums', | |
| 393 | + 'delete_forums', | |
| 394 | + 'delete_others_forums', | |
| 395 | + 'edit_topics', | |
| 396 | + 'edit_others_topics', | |
| 397 | + 'publish_topics', | |
| 398 | + 'read_private_topics', | |
| 399 | + 'delete_topics', | |
| 400 | + 'delete_others_topics', | |
| 401 | + ); | |
| 402 | + | |
| 403 | + foreach ( $bbpress_caps as $cap ) { | |
| 404 | + $allcaps[ $cap ] = true; | |
| 405 | + } | |
| 406 | + | |
| 407 | + return $allcaps; | |
| 408 | +} | |
| 409 | +add_filter( 'user_has_cap', 'forumax_grant_bbpress_caps_to_admins', 10, 3 ); | |
| 410 | + | |
| 411 | +/** | |
| 317 | 412 | * Mutual Deactivation of Forumax and bbp-core plugins |
| 318 | 413 | */ |
| 319 | 414 | add_action( 'activated_plugin', function( $plugin ) { |
| 320 | 415 | if ( ! function_exists( 'is_plugin_active' ) ) { |
| @@ -369,11 +464,19 @@ | ||
| 369 | 464 | ] ); |
| 370 | 465 | }, 20 ); // Priority 20 to run after theme's widgets_init |
| 371 | 466 | |
| 372 | 467 | |
| 468 | +/** | |
| 469 | + * Add title and thumbnail support for bbPress forum and topic post types. | |
| 470 | + * | |
| 471 | + * Ensures the title field and featured image meta box are available | |
| 472 | + * in the WordPress admin editor for both post types. | |
| 473 | + */ | |
| 373 | 474 | add_action( 'init', function () { |
| 374 | - add_post_type_support( 'forum', 'thumbnail' ); | |
| 375 | - add_post_type_support( 'topic', 'thumbnail' ); | |
| 475 | + add_post_type_support( 'forum', 'title' ); | |
| 476 | + add_post_type_support( 'forum', 'thumbnail' ); | |
| 477 | + add_post_type_support( 'topic', 'title' ); | |
| 478 | + add_post_type_support( 'topic', 'thumbnail' ); | |
| 376 | 479 | }, 25 ); |
| 377 | 480 | |
| 378 | 481 | |
| 379 | 482 | /** |
| @@ -494,8 +597,20 @@ | ||
| 494 | 597 | */ |
| 495 | 598 | function forumax_get_topic_count_by_status( $status = 'publish', $parent_id = false ) { |
| 496 | 599 | global $wpdb; |
| 497 | 600 | |
| 601 | + // Sanitize parameters for cache key | |
| 602 | + $cache_status = sanitize_key( $status ); | |
| 603 | + $cache_parent = ( false !== $parent_id && is_numeric( $parent_id ) ) ? (int) $parent_id : 0; | |
| 604 | + | |
| 605 | + // Check transient cache | |
| 606 | + $cache_key = 'frmx_topic_count_' . $cache_status . '_' . $cache_parent; | |
| 607 | + $count = get_transient( $cache_key ); | |
| 608 | + | |
| 609 | + if ( false !== $count ) { | |
| 610 | + return (int) $count; | |
| 611 | + } | |
| 612 | + | |
| 498 | 613 | $status = esc_sql( $status ); |
| 499 | 614 | $where = "WHERE post_type = 'topic' AND post_status = '{$status}'"; |
| 500 | 615 | |
| 501 | 616 | if ( false !== $parent_id && is_numeric( $parent_id ) ) { |
| @@ -503,6 +618,84 @@ | ||
| 503 | 618 | } |
| 504 | 619 | |
| 505 | 620 | $count = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->posts} {$where}" ); |
| 506 | 621 | |
| 622 | + // Set transient cache for 1 hour | |
| 623 | + set_transient( $cache_key, (int) $count, HOUR_IN_SECONDS ); | |
| 624 | + | |
| 507 | 625 | return (int) $count; |
| 508 | 626 | } |
| 627 | + | |
| 628 | +/** | |
| 629 | + * Get unanswered topics count. | |
| 630 | + * | |
| 631 | + * @return int The number of unanswered topics. | |
| 632 | + */ | |
| 633 | +function forumax_get_unanswered_topics_count() { | |
| 634 | + $cache_key = 'frmx_unanswered_topics_count'; | |
| 635 | + $count = wp_cache_get( $cache_key, 'forumax' ); | |
| 636 | + | |
| 637 | + if ( false !== $count ) { | |
| 638 | + return $count; | |
| 639 | + } | |
| 640 | + | |
| 641 | + global $wpdb; | |
| 642 | + $count = $wpdb->get_var( | |
| 643 | + "SELECT COUNT(*) FROM {$wpdb->posts} p | |
| 644 | + LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_bbp_reply_count' | |
| 645 | + WHERE p.post_type = 'topic' | |
| 646 | + AND p.post_status = 'publish' | |
| 647 | + AND (pm.meta_value IS NULL OR pm.meta_value = '0')" | |
| 648 | + ); | |
| 649 | + | |
| 650 | + wp_cache_set( $cache_key, $count, 'forumax', HOUR_IN_SECONDS ); | |
| 651 | + return absint( $count ); | |
| 652 | +} | |
| 653 | + | |
| 654 | +/** | |
| 655 | + * Get recent unanswered topics. | |
| 656 | + * | |
| 657 | + * @param int $limit Number of topics to retrieve. | |
| 658 | + * @return array Array of WP_Post objects. | |
| 659 | + */ | |
| 660 | +function forumax_get_recent_unanswered_topics( $limit = 5 ) { | |
| 661 | + $args = [ | |
| 662 | + 'post_type' => 'topic', | |
| 663 | + 'post_status' => 'publish', | |
| 664 | + 'posts_per_page' => $limit, | |
| 665 | + 'orderby' => 'date', | |
| 666 | + 'order' => 'DESC', | |
| 667 | + 'meta_query' => [ | |
| 668 | + 'relation' => 'OR', | |
| 669 | + [ | |
| 670 | + 'key' => '_bbp_reply_count', | |
| 671 | + 'value' => '0', | |
| 672 | + 'compare' => '=', | |
| 673 | + ], | |
| 674 | + [ | |
| 675 | + 'key' => '_bbp_reply_count', | |
| 676 | + 'compare' => 'NOT EXISTS', | |
| 677 | + ], | |
| 678 | + ], | |
| 679 | + ]; | |
| 680 | + | |
| 681 | + return get_posts( $args ); | |
| 682 | +} | |
| 683 | + | |
| 684 | +/** | |
| 685 | + * Invalidate unanswered topics cache. | |
| 686 | + */ | |
| 687 | +function forumax_invalidate_unanswered_topics_cache() { | |
| 688 | + wp_cache_delete( 'frmx_unanswered_topics_count', 'forumax' ); | |
| 689 | +} | |
| 690 | +add_action( 'bbp_new_reply', 'forumax_invalidate_unanswered_topics_cache' ); | |
| 691 | +add_action( 'bbp_deleted_reply', 'forumax_invalidate_unanswered_topics_cache' ); | |
| 692 | +add_action( 'bbp_trash_reply', 'forumax_invalidate_unanswered_topics_cache' ); | |
| 693 | +add_action( 'bbp_untrash_reply', 'forumax_invalidate_unanswered_topics_cache' ); | |
| 694 | +add_action( 'bbp_spam_reply', 'forumax_invalidate_unanswered_topics_cache' ); | |
| 695 | +add_action( 'bbp_unspam_reply', 'forumax_invalidate_unanswered_topics_cache' ); | |
| 696 | +add_action( 'bbp_new_topic', 'forumax_invalidate_unanswered_topics_cache' ); | |
| 697 | +add_action( 'bbp_deleted_topic', 'forumax_invalidate_unanswered_topics_cache' ); | |
| 698 | +add_action( 'bbp_trash_topic', 'forumax_invalidate_unanswered_topics_cache' ); | |
| 699 | +add_action( 'bbp_untrash_topic', 'forumax_invalidate_unanswered_topics_cache' ); | |
| 700 | +add_action( 'bbp_spam_topic', 'forumax_invalidate_unanswered_topics_cache' ); | |
| 701 | +add_action( 'bbp_unspam_topic', 'forumax_invalidate_unanswered_topics_cache' ); | |