PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.2.0
Forumax – AI Powered Advanced Community Forum Plugin v2.2.0
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
← All changes | includes/functions.php +525 -26 1.3.22.2.0 View file →
@@ -7,9 +7,9 @@
7 7 * @param string $default default text if it's not found
8 8 *
9 9 * @return mixed
10 10 */
11 -function bbpc_get_opt( $option, $default = '' ) {
11 +function forumax_get_opt( $option, $default = '' ) {
12 12 $options = get_option( 'bbp_core_settings' );
13 13
14 14 if ( isset( $options[ $option ] ) ) {
15 15 return $options[ $option ];
@@ -24,9 +24,9 @@
24 24 * @param string $plugin_path The plugin path (e.g. 'woocommerce/woocommerce.php')
25 25 * @param int $days Number of days to check against
26 26 * @return bool True if plugin is installed for specified days, false otherwise
27 27 */
28 -function bbpc_is_plugin_installed_for_days( $days, $plugin_slug='eazydocs' ) {
28 +function forumax_is_plugin_installed_for_days( $days, $plugin_slug='eazydocs' ) {
29 29 // Get the installation timestamp of the plugin
30 30 $installed_time = get_option( $plugin_slug . '_installed' );
31 31
32 32 // Ensure it's a valid timestamp
@@ -46,45 +46,78 @@
46 46
47 47 /**
48 48 * Check If the Page is Forum page
49 49 */
50 -function bbpc_is_forum_page() {
51 - if ( in_array( 'bbpress', get_body_class() ) ) {
50 +function forumax_is_forum_page() {
51 + if ( in_array( 'bbpress', get_body_class(), true ) ) {
52 52 return true;
53 53 }
54 54 }
55 55
56 56 /**
57 + * Turn on the WordPress visual editor for bbPress
58 + *
59 + * @param array $args
60 + * @return array
61 + */
62 +function forumax_bbp_enable_visual_editor( $args = [] ) {
63 + $args['tinymce'] = true;
64 + return $args;
65 +}
66 +add_filter( 'bbp_after_get_the_content_parse_args', 'forumax_bbp_enable_visual_editor' );
67 +
68 +/**
57 69 * Check if the pro plugin and plan is active
58 70 *
59 71 * @return bool|void
60 72 */
61 -function bbpc_is_premium() {
62 - if ( class_exists('BBPCorePro') && bc_fs()->can_use_premium_code() ) {
73 +function forumax_is_premium() {
74 + if ( class_exists('Forumax_Pro') && bc_fs()->can_use_premium_code() ) {
63 75 return true;
64 76 }
65 77 }
66 78
67 79 /**
68 - * BBP Core Admin pages
69 - * If any of the admin pages match the current page, return true.
80 + * Check if the promax plan is active
70 81 *
71 82 * @return bool|void
72 83 */
73 -function bbpc_admin_pages($admin) {
74 - $current_url = ! empty( $_GET['page'] ) ? admin_url( 'admin.php?page=' ) . sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
84 +function forumax_is_promax() {
85 + if ( class_exists('Forumax_Pro') && bc_fs()->can_use_premium_code() && bc_fs()->is_plan('promax') ) {
86 + return true;
87 + }
88 +}
75 89
76 - if ( $admin == 'admin' ){
77 - if ( $current_url == admin_url('admin.php?page=bbp-core') ) {
90 +/**
91 + * Forumax Admin pages
92 + *
93 + * Checks if the current admin page matches the specified Forumax page type.
94 + *
95 + * @param string $admin The admin page type to check ('admin', 'settings', 'dashboard').
96 + *
97 + * @return bool True if on the specified admin page, false otherwise.
98 + */
99 +function forumax_admin_pages( $admin ) {
100 + $current_url = ! empty( $_GET['page'] ) ? admin_url( 'admin.php?page=' ) . sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
101 +
102 + if ( 'admin' === $admin ) {
103 + if ( admin_url( 'admin.php?page=forumax-builder' ) === $current_url ) {
78 104 return true;
79 105 }
80 - } elseif ( $admin == 'settings' ) {
81 - if ( $current_url == admin_url('admin.php?page=bbp-core-settings') ) {
106 + } elseif ( 'settings' === $admin ) {
107 + if ( admin_url( 'admin.php?page=forumax-settings' ) === $current_url ) {
82 108 return true;
83 109 }
110 + } elseif ( 'dashboard' === $admin ) {
111 + if ( admin_url( 'admin.php?page=forumax' ) === $current_url ) {
112 + return true;
113 + }
84 114 }
115 +
116 + return false;
85 117 }
86 118
119 +
87 120 /**
88 121 * BBP Forum Assets
89 122 * Checks if the current page is a single forum or a single topic.
90 123 *
@@ -89,10 +122,10 @@
89 122 * Checks if the current page is a single forum or a single topic.
90 123 *
91 124 * @return bool True if the current page is a single forum or topic, false otherwise.
92 125 */
93 -function bbpc_forum_and_topic_page(){
94 - if ( bbp_is_single_forum() || bbp_is_single_topic() ) {
126 +function forumax_forum_and_topic_page(){
127 + if ( bbp_is_single_forum() || bbp_is_single_topic() || bbp_is_reply_edit()) {
95 128 return true;
96 129 }
97 130 }
98 131
@@ -100,9 +133,9 @@
100 133 /**
101 134 * Posts Arraty
102 135 * @param object Post Type
103 136 */
104 -function bbp_core_get_posts( $post_type = 'forum' ) {
137 +function forumax_get_posts( $post_type = 'forum' ) {
105 138 $posts = get_pages(
106 139 [
107 140 'post_type' => $post_type,
108 141 'parent' => 0,
@@ -125,13 +158,13 @@
125 158 * @param $string
126 159 * @param $limit_length
127 160 * @param string $suffix
128 161 */
129 -function bbp_core_limit_letter( $string, $limit_length, $suffix = '...' ) {
162 +function forumax_limit_letter( $string, $limit_length, $suffix = '...' ) {
130 163 if ( strlen( $string ) > $limit_length ) {
131 - echo strip_shortcodes( substr( $string, 0, $limit_length ) . $suffix );
164 + echo esc_html ( strip_shortcodes( substr( $string, 0, $limit_length ) . $suffix ) );
132 165 } else {
133 - echo strip_shortcodes( esc_html( $string ) );
166 + echo esc_html( $string );
134 167 }
135 168 }
136 169
137 170 /**
@@ -142,9 +175,9 @@
142 175 * @return int The view count
143 176 * @uses get_post_meta() To get the view count meta
144 177 * @uses bbp_get_topic_id() To get the topic id
145 178 */
146 -function bbp_get_topic_view_count( $topic_id = 0 ) {
179 +function forumax_get_topic_view_count( $topic_id = 0 ) {
147 180 $topic_id = bbp_get_topic_id( $topic_id );
148 181
149 182 if ( empty( $topic_id ) ) {
150 183 return 0;
@@ -162,19 +195,85 @@
162 195 *
163 196 * @uses bbp_get_topic_id() To get the topic id
164 197 * @uses btv_get_topic_view_count() To get the view count for the topic
165 198 */
166 -function bbp_topic_view_count( $topic_id = 0 ) {
199 +function forumax_topic_view_count( $topic_id = 0 ) {
167 200 $topic_id = bbp_get_topic_id( $topic_id );
168 - $view_count = bbp_get_topic_view_count( $topic_id );
201 + $view_count = forumax_get_topic_view_count( $topic_id );
169 202 return $view_count;
170 203 }
171 204
172 205 /**
206 + * Increment the topic view count.
207 + *
208 + * Increments the view count when a visitor views a topic.
209 + * Uses cookies to prevent duplicate counts within a session (1 hour).
210 + *
211 + * @param int $topic_id Topic ID.
212 + * @return bool True if view was counted, false otherwise.
213 + */
214 +function forumax_increment_topic_view_count( $topic_id = 0 ) {
215 + $topic_id = bbp_get_topic_id( $topic_id );
216 +
217 + if ( empty( $topic_id ) ) {
218 + return false;
219 + }
220 +
221 + // Optionally: Don't count views for logged-in admins/moderators
222 + // Uncomment the following to exclude admin views from being counted
223 + // if ( current_user_can( 'moderate' ) ) {
224 + // return false;
225 + // }
226 +
227 + // Use a cookie to prevent duplicate counts within the same session
228 + $cookie_name = 'forumax_viewed_' . $topic_id;
229 +
230 + // Check if this topic was already viewed in this session
231 + if ( isset( $_COOKIE[ $cookie_name ] ) ) {
232 + return false;
233 + }
234 +
235 + // Get current view count
236 + $current_views = (int) get_post_meta( $topic_id, '_btv_view_count', true );
237 +
238 + // Increment the view count
239 + $new_views = $current_views + 1;
240 +
241 + // Update the view count
242 + update_post_meta( $topic_id, '_btv_view_count', $new_views );
243 +
244 + // Set cookie to prevent duplicate counting (expires in 1 hour)
245 + setcookie( $cookie_name, '1', time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
246 +
247 + return true;
248 +}
249 +
250 +/**
251 + * Track topic views on template redirect.
252 + *
253 + * Hooks into bbPress template redirect to track topic views
254 + * when a single topic page is loaded.
255 + */
256 +function forumax_track_topic_views() {
257 + // Only track on single topic pages
258 + if ( ! function_exists( 'bbp_is_single_topic' ) || ! bbp_is_single_topic() ) {
259 + return;
260 + }
261 +
262 + // Get the current topic ID
263 + $topic_id = bbp_get_topic_id();
264 +
265 + if ( ! empty( $topic_id ) ) {
266 + forumax_increment_topic_view_count( $topic_id );
267 + }
268 +}
269 +add_action( 'bbp_template_redirect', 'forumax_track_topic_views', 20 );
270 +
271 +/**
173 272 * Get forum title
174 273 * @return string
175 274 */
176 -function bbpc_forum_title(){
275 +function forumax_forum_title(){
177 276 $forum_id = bbp_get_forum_id();
178 277 $forum_title = get_the_title( $forum_id );
179 278 return $forum_title;
180 279 }
@@ -189,12 +288,12 @@
189 288
190 289 /**
191 290 * Get all the registered menus
192 291 */
193 -function bbpc_get_registered_nav_menus() {
292 +function forumax_get_registered_nav_menus() {
194 293 $menus = get_registered_nav_menus();
195 294 $menu_locations = [];
196 - $empty = [ '' => esc_html__('Select Menu Location', 'bbp-core') ];
295 + $empty = [ '' => esc_html__('Select Menu Location', 'forumax') ];
197 296 foreach ( $menus as $location => $description ) {
198 297 $menu_locations[ $location ] = $description;
199 298 }
200 299
@@ -199,4 +298,404 @@
199 298 }
200 299
201 300 return $empty + $menu_locations;
202 301 }
302 +
303 +/**
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.
313 + */
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;
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;
363 +}
364 +add_filter( 'register_post_type_args', 'forumax_fix_post_type_args', 99, 2 );
365 +
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 +/**
412 + * Mutual Deactivation of Forumax and bbp-core plugins
413 + */
414 +add_action( 'activated_plugin', function( $plugin ) {
415 + if ( ! function_exists( 'is_plugin_active' ) ) {
416 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
417 + }
418 +
419 + // All Forumax plugins
420 + $forumax_plugins = [
421 + 'forumax/forumax.php',
422 + 'forumax-pro/forumax.php',
423 + ];
424 +
425 + // All bbp-core plugins
426 + $bbp_core_plugins = [
427 + 'bbp-core/bbp-core.php',
428 + 'bbp-core-pro/bbp-core.php',
429 + ];
430 +
431 + // If activating Forumax (free or pro) → deactivate bbp-core (all versions)
432 + if ( in_array( $plugin, $forumax_plugins, true ) ) {
433 + deactivate_plugins( $bbp_core_plugins );
434 + }
435 +
436 +
437 + // If activating bbp-core (free or pro) → deactivate Forumax (all versions)
438 + if ( in_array( $plugin, $bbp_core_plugins, true ) ) {
439 + deactivate_plugins( $forumax_plugins );
440 + }
441 +
442 +} );
443 +
444 +/**
445 + * Register Forum Sidebar widget area
446 + * This makes the Forum Sidebar available for any theme, not just theme-specific implementations
447 + */
448 +add_action( 'widgets_init', function () {
449 + global $wp_registered_sidebars;
450 +
451 + // Check if the sidebar is already registered by the theme (e.g., Docy)
452 + if ( isset( $wp_registered_sidebars['forum_archive_sidebar'] ) ) {
453 + return;
454 + }
455 +
456 + register_sidebar( [
457 + 'name' => esc_html__( 'Forumax Sidebar', 'forumax' ),
458 + 'description' => esc_html__( 'Add widgets here for the Forumax Sidebar area', 'forumax' ),
459 + 'id' => 'forum_archive_sidebar',
460 + 'before_widget' => '<div id="%1$s" class="widget sidebar_widget %2$s">',
461 + 'after_widget' => '</div>',
462 + 'before_title' => '<h3 class="widget-title">',
463 + 'after_title' => '</h3>'
464 + ] );
465 +}, 20 ); // Priority 20 to run after theme's widgets_init
466 +
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 + */
474 +add_action( 'init', function () {
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' );
479 +}, 25 );
480 +
481 +
482 +/**
483 + * Get moderator and keymaster users for assistant selection.
484 + *
485 + * @return array Array of user ID => display name.
486 + */
487 +if ( ! function_exists( 'frmx_get_moderator_users' ) ) {
488 + function frmx_get_moderator_users() {
489 + $users = [];
490 +
491 + // Get users with bbPress moderator or keymaster roles.
492 + $args = [
493 + 'role__in' => [ 'bbp_moderator', 'bbp_keymaster', 'administrator' ],
494 + 'orderby' => 'display_name',
495 + 'order' => 'ASC',
496 + 'number' => 100,
497 + ];
498 +
499 + $user_query = new WP_User_Query( $args );
500 +
501 + if ( ! empty( $user_query->get_results() ) ) {
502 + foreach ( $user_query->get_results() as $user ) {
503 +
504 + $role_display = '';
505 +
506 + if ( in_array( 'bbp_keymaster', (array) $user->roles, true ) ) {
507 + $role_display = __( 'Keymaster', 'forumax' );
508 + } elseif ( in_array( 'bbp_moderator', (array) $user->roles, true ) ) {
509 + $role_display = __( 'Moderator', 'forumax' );
510 + } elseif ( in_array( 'administrator', (array) $user->roles, true ) ) {
511 + $role_display = __( 'Admin', 'forumax' );
512 + }
513 +
514 + $users[$user->ID] = sprintf(
515 + '%s (%s)',
516 + $user->display_name,
517 + $role_display
518 + );
519 + }
520 + }
521 +
522 + return $users;
523 + }
524 +}
525 +
526 +/**
527 + * Remove all admin notices on Forumax admin pages.
528 + *
529 + * Cleans up the admin interface by hiding third-party notices
530 + * on all Forumax-related admin pages.
531 + *
532 + * @since 1.0.0
533 + * @return void
534 + */
535 +function forumax_remove_admin_notices() {
536 + // Check if we're on any Forumax admin page.
537 + $is_forumax_page = false;
538 +
539 + // Check using forumax_admin_pages() for known page types.
540 + if ( function_exists( 'forumax_admin_pages' ) ) {
541 + $is_forumax_page = forumax_admin_pages( 'admin' )
542 + || forumax_admin_pages( 'settings' )
543 + || forumax_admin_pages( 'dashboard' );
544 + }
545 +
546 + // Also check for Analytics and other forumax-* pages.
547 + $current_page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
548 + if ( strpos( $current_page, 'forumax' ) === 0 ) {
549 + $is_forumax_page = true;
550 + }
551 +
552 + if ( $is_forumax_page ) {
553 + remove_all_actions( 'admin_notices' );
554 + remove_all_actions( 'all_admin_notices' );
555 + }
556 +}
557 +add_action( 'admin_head', 'forumax_remove_admin_notices' );
558 +
559 +/**
560 + * Hide theme sidebars on bbPress pages
561 + *
562 + * This ensures only the Forumax sidebar displays on forum pages,
563 + * preventing conflicts with theme sidebars (Divi, Astra, etc.).
564 + *
565 + * @param array $sidebars_widgets Array of sidebar widgets.
566 + * @return array Modified array with theme sidebars emptied on bbPress pages.
567 + */
568 +function forumax_hide_theme_sidebars( $sidebars_widgets ) {
569 + // Only modify on frontend bbPress pages
570 + if ( is_admin() || ! function_exists( 'is_bbpress' ) || ! is_bbpress() ) {
571 + return $sidebars_widgets;
572 + }
573 +
574 + // Keep only the Forumax sidebar, hide all other theme sidebars
575 + foreach ( $sidebars_widgets as $sidebar_id => $widgets ) {
576 + // Skip wp_inactive_widgets and our own forum sidebar
577 + // We also skip footer sidebars to ensure footer widgets are visible
578 + if ( 'wp_inactive_widgets' === $sidebar_id || 'forum_archive_sidebar' === $sidebar_id || strpos( $sidebar_id, 'footer' ) !== false ) {
579 + continue;
580 + }
581 +
582 + // Empty other sidebars (this prevents them from rendering)
583 + $sidebars_widgets[ $sidebar_id ] = [];
584 + }
585 +
586 + return $sidebars_widgets;
587 +}
588 +add_filter( 'sidebars_widgets', 'forumax_hide_theme_sidebars' );
589 +
590 +/**
591 + * Get topic count by status.
592 + *
593 + * @param string $status The post status to count (e.g., 'publish', 'closed').
594 + * @param int|bool $parent_id Optional. The parent forum ID to filter by.
595 + *
596 + * @return int The number of topics with the specified status.
597 + */
598 +function forumax_get_topic_count_by_status( $status = 'publish', $parent_id = false ) {
599 + global $wpdb;
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 +
613 + $status = esc_sql( $status );
614 + $where = "WHERE post_type = 'topic' AND post_status = '{$status}'";
615 +
616 + if ( false !== $parent_id && is_numeric( $parent_id ) ) {
617 + $where .= $wpdb->prepare( " AND post_parent = %d", $parent_id );
618 + }
619 +
620 + $count = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->posts} {$where}" );
621 +
622 + // Set transient cache for 1 hour
623 + set_transient( $cache_key, (int) $count, HOUR_IN_SECONDS );
624 +
625 + return (int) $count;
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' );