current_topic_id; if ( $current > 0 ) { return $current; } } $object = get_queried_object(); if ( $object instanceof WP_Post && function_exists( 'bbp_get_topic_post_type' ) && bbp_get_topic_post_type() === $object->post_type ) { return (int) $object->ID; } return 0; } /** * Seed current_topic_id as soon as the main query is a topic. * * @return void */ function forumax_prime_current_topic_id() { if ( ! function_exists( 'bbpress' ) || ! function_exists( 'bbp_get_topic_post_type' ) ) { return; } if ( ! empty( bbpress()->current_topic_id ) ) { return; } if ( is_singular( bbp_get_topic_post_type() ) ) { $topic_id = (int) get_queried_object_id(); if ( $topic_id > 0 ) { bbpress()->current_topic_id = $topic_id; } } } add_action( 'wp', 'forumax_prime_current_topic_id', 1 ); /** * Treat a primed topic ID as a single-topic view. * * @param bool $is_single_topic Current detection result. * @return bool */ function forumax_filter_is_single_topic( $is_single_topic ) { if ( $is_single_topic ) { return true; } if ( function_exists( 'bbp_is_topic_edit' ) && bbp_is_topic_edit() ) { return false; } return forumax_resolve_current_topic_id() > 0; } add_filter( 'bbp_is_single_topic', 'forumax_filter_is_single_topic' ); /** * Fall back to the primed / queried topic when bbPress cannot resolve one. * * @param int $topic_id Resolved topic ID. * @param int $requested ID passed into bbp_get_topic_id(). * @return int */ function forumax_filter_get_topic_id( $topic_id, $requested ) { if ( ! empty( $requested ) && is_numeric( $requested ) ) { return (int) $topic_id; } if ( (int) $topic_id > 0 ) { return (int) $topic_id; } return forumax_resolve_current_topic_id(); } add_filter( 'bbp_get_topic_id', 'forumax_filter_get_topic_id', 10, 2 ); /** * Never run an unscoped replies query while a topic is being rendered. * * User-profile and author queries keep post_parent=any. Hierarchical * site-wide queries are also refused so posts_per_page cannot become -1. * * @param array $args Reply query args after parse. * @return array */ function forumax_scope_has_replies_query( $args ) { if ( ! is_array( $args ) ) { return $args; } $explicit_parent = isset( $args['post_parent'] ) && is_numeric( $args['post_parent'] ) && (int) $args['post_parent'] > 0; $is_author_query = ! empty( $args['author'] ) || ! empty( $args['author__in'] ); $is_id_query = ! empty( $args['post__in'] ) || ! empty( $args['post__not_in'] ); if ( ! $explicit_parent && ! $is_author_query && ! $is_id_query ) { $topic_id = forumax_resolve_current_topic_id(); if ( $topic_id > 0 ) { $args['post_parent'] = $topic_id; } } $parent = isset( $args['post_parent'] ) ? $args['post_parent'] : ''; if ( ! empty( $args['hierarchical'] ) && ( ! is_numeric( $parent ) || (int) $parent < 1 ) ) { $args['hierarchical'] = false; if ( isset( $args['posts_per_page'] ) && (int) $args['posts_per_page'] < 1 ) { $args['posts_per_page'] = function_exists( 'bbp_get_replies_per_page' ) ? (int) bbp_get_replies_per_page() : 20; } } return $args; } add_filter( 'bbp_has_replies_query', 'forumax_scope_has_replies_query', 1 ); /** * Build a cache key fragment for a map_meta_cap argument. * * Gutenberg passes objects such as WP_Block_Editor_Context. Those cannot be * cast with strval() and must not fatal the post editor. * * @param mixed $arg Raw map_meta_cap argument. * @return string */ function forumax_map_meta_cap_arg_fragment( $arg ) { if ( is_null( $arg ) || is_bool( $arg ) || is_int( $arg ) || is_float( $arg ) || is_string( $arg ) ) { return (string) $arg; } if ( is_object( $arg ) ) { return get_class( $arg ) . '#' . spl_object_id( $arg ); } if ( is_array( $arg ) ) { $parts = array(); foreach ( $arg as $key => $value ) { $parts[] = forumax_map_meta_cap_arg_fragment( $key ) . '=' . forumax_map_meta_cap_arg_fragment( $value ); } return '[' . implode( ',', $parts ) . ']'; } return gettype( $arg ); } /** * Cache bbPress meta-cap maps for the current request and break re-entry. * * The bundled mappers call user_can() from inside map_meta_cap. On a topic * page that repeats the same checks per reply/admin link until memory dies. * * @param string[] $caps Primitive caps. * @param string $cap Meta capability. * @param int $user_id User ID. * @param array $args Extra arguments (usually a post ID). * @return string[] */ function forumax_cache_bbp_map_meta_caps( $caps, $cap, $user_id, $args ) { static $cache = array(); static $stack = array(); $arg_key = ''; if ( ! empty( $args ) && is_array( $args ) ) { $arg_key = implode( ',', array_map( 'forumax_map_meta_cap_arg_fragment', $args ) ); } $key = (int) $user_id . '|' . (string) $cap . '|' . $arg_key; if ( isset( $cache[ $key ] ) ) { return $cache[ $key ]; } // Same cap is already being mapped further up the stack. if ( isset( $stack[ $key ] ) ) { return $caps; } $stack[ $key ] = true; $mapped = bbp_map_meta_caps( $caps, $cap, $user_id, $args ); unset( $stack[ $key ] ); $cache[ $key ] = $mapped; return $mapped; } /** * Replace the stock bbPress map_meta_cap aggregator with a cached wrapper. * * @return void */ function forumax_install_map_meta_cap_cache() { if ( ! function_exists( 'bbp_map_meta_caps' ) ) { return; } remove_filter( 'map_meta_cap', 'bbp_map_meta_caps', 10 ); add_filter( 'map_meta_cap', 'forumax_cache_bbp_map_meta_caps', 10, 4 ); } forumax_install_map_meta_cap_cache();