PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.4.2
Forumax – AI Powered Advanced Community Forum Plugin v2.4.2
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 / classes / Forumax_Forum_Class.php

Forumax_Forum_Class.php in Forumax – AI Powered Advanced Community Forum Plugin 2.4.2, at includes/classes/Forumax_Forum_Class.php

359 lines 14.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Forumax theme helper functions and resources
4 */
5 if ( !class_exists( 'Forumax_Forum_Class' ) ) {
6 class Forumax_Forum_Class {
7 /**
8 * Hold an instance of Forumax_Forum_Class class.
9 * @var Forumax_Forum_Class
10 */
11 protected static $instance = null;
12
13 /**
14 * Main Forumax_Forum_Class instance.
15 * @return Forumax_Forum_Class - Main instance.
16 */
17 public static function instance() {
18
19 if ( null === self::$instance ) {
20 self::$instance = new Forumax_Forum_Class();
21 }
22
23 return self::$instance;
24 }
25 /**
26 * Adds a reference of this object to $instance, populates default strings,
27 * @see __construct
28 */
29 public function __construct() {
30 add_filter( 'the_posts', [ $this, 'inject_sticky_topics' ], 10, 2 );
31
32 // Cache invalidation hooks
33 add_action( 'bbp_new_topic', [ $this, 'invalidate_forum_caches' ], 10, 2 );
34 add_action( 'bbp_edit_topic', [ $this, 'invalidate_forum_caches' ], 10, 2 );
35 add_action( 'bbp_closed_topic', [ $this, 'invalidate_forum_caches' ], 10, 2 );
36 add_action( 'bbp_opened_topic', [ $this, 'invalidate_forum_caches' ], 10, 2 );
37 add_action( 'bbp_trash_topic', [ $this, 'maybe_invalidate_forum_caches' ] );
38 add_action( 'bbp_untrash_topic', [ $this, 'maybe_invalidate_forum_caches' ] );
39 add_action( 'bbp_deleted_topic', [ $this, 'maybe_invalidate_forum_caches' ] );
40 }
41
42 /**
43 * Authors Name
44 *
45 * @return string
46 */
47 public static function forum_topics_authors() {
48 global $wpdb;
49
50 $forum_id = get_queried_object_id();
51 $cache_key = 'frmx_authors_data_' . $forum_id;
52 $display_authors = get_transient( $cache_key );
53
54 if ( false === $display_authors ) {
55 $d_users = [];
56 $forumusers = get_users();
57
58 if ( $forumusers ) {
59 foreach ( $forumusers as $forumuser ) {
60 $post_count = count_user_posts( $forumuser->ID, 'topic' );
61 $d_users[$forumuser->ID] = $post_count;
62 }
63 arsort( $d_users );
64
65 // Optimization: Fetch all counts for the current parent in one query
66 $author_counts = [];
67 if ( ! empty( $d_users ) ) {
68 $sql = $wpdb->prepare( "
69 SELECT post_author, COUNT(*) as count
70 FROM $wpdb->posts
71 WHERE post_type = 'topic'
72 AND post_status = 'publish'
73 AND post_parent = %d
74 GROUP BY post_author
75 ", $forum_id );
76
77 $results = $wpdb->get_results( $sql );
78 if ( $results ) {
79 foreach ( $results as $row ) {
80 $author_counts[ $row->post_author ] = $row->count;
81 }
82 }
83 }
84
85 $display_authors = [];
86 foreach ( $d_users as $key => $value ) {
87 // Use pre-fetched count
88 $users_post_count = isset( $author_counts[ $key ] ) ? $author_counts[ $key ] : 0;
89
90 if ( $users_post_count > 0 ) {
91 $display_authors[] = [
92 'ID' => $key,
93 'count' => $users_post_count
94 ];
95 }
96 }
97
98 set_transient( $cache_key, $display_authors, HOUR_IN_SECONDS );
99 }
100 }
101
102 if ( ! empty( $display_authors ) ) {
103 foreach ( $display_authors as $author_data ) {
104 $user_id = $author_data['ID'];
105 $user = get_userdata( $user_id );
106
107 if ( ! $user ) {
108 continue;
109 }
110
111 $post_count = $author_data['count'];
112
113 echo '<div class="userlist">';
114 echo '<a class="frmx-dropdown-item current-user forumax-data data-auth" data-nonce="' . wp_create_nonce( 'forumax-nonce' ) . '" data-rel="' . esc_attr( $user->display_name ) . '" data-type="author" data-count="' . esc_attr( $post_count ) . '" data-parent="' . esc_attr( $forum_id ) . '" data-id="' . intval( $user_id ) . '" href="#' . esc_attr( $user->display_name ) . '">
115 <img src="' . esc_url( get_avatar_url( $user_id ) ) . '" alt="' . esc_attr( $user->display_name ) . '">' . esc_html( $user->display_name ) . '<span class="count">' . esc_html( $post_count ) . '</span>
116 </a>';
117 echo '</div>';
118 }
119 }
120 }
121
122 /**
123 * Tag Name
124 *
125 * @return string
126 */
127 public static function forum_topics_tags() {
128 global $wpdb;
129
130 $forum_id = get_queried_object_id();
131 $cache_key = 'frmx_tags_data_' . $forum_id;
132 $tags = get_transient( $cache_key );
133
134 if ( false === $tags ) {
135 $tags = $wpdb->get_results( $wpdb->prepare(
136 "SELECT DISTINCT t.term_id, t.name, t.slug, tt.count
137 FROM {$wpdb->terms} AS t
138 INNER JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id
139 INNER JOIN {$wpdb->term_relationships} AS tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
140 INNER JOIN {$wpdb->posts} AS p ON tr.object_id = p.ID
141 WHERE tt.taxonomy = 'topic-tag'
142 AND p.post_type = 'topic'
143 AND p.post_parent = %d
144 AND p.post_status = 'publish'
145 ORDER BY t.name ASC",
146 $forum_id
147 ) );
148
149 set_transient( $cache_key, $tags, HOUR_IN_SECONDS );
150 }
151
152 if ( $tags ) {
153 foreach ( $tags as $tag ) {
154 $post_count = $tag->count;
155 echo '<div class="tagList" id="' . esc_attr( $tag->slug ) . '">';
156 echo '<a class="frmx-dropdown-item forumax-data data-tag" data-parent="' . esc_attr( $forum_id ) . '" data-nonce="' . wp_create_nonce( 'forumax-nonce' ) . '" data-rel="' . esc_attr( $tag->slug ) . '" data-type="tag" data-count="' . esc_attr( $post_count ) . '" data-id="' . intval( $tag->term_id ) . '" href="#' . esc_attr( $tag->slug ) . '"><span class="color-purple"></span>' . esc_html( $tag->name ) . '</a>';
157 echo '</div>';
158 }
159 }
160 }
161
162 /**
163 * Invalidate forum caches when a topic is updated.
164 *
165 * @param int $topic_id Topic ID.
166 * @param int $forum_id Optional. Forum ID.
167 */
168 public function invalidate_forum_caches( $topic_id, $forum_id = 0 ) {
169 if ( ! $forum_id ) {
170 $forum_id = bbp_get_topic_forum_id( $topic_id );
171 }
172
173 if ( $forum_id ) {
174 delete_transient( 'frmx_authors_data_' . $forum_id );
175 delete_transient( 'frmx_tags_data_' . $forum_id );
176 delete_transient( 'frmx_topic_count_publish_' . $forum_id );
177 delete_transient( 'frmx_topic_count_closed_' . $forum_id );
178
179 // Also invalidate global topic counts (forum_id = 0) used on main archive
180 delete_transient( 'frmx_topic_count_publish_0' );
181 delete_transient( 'frmx_topic_count_closed_0' );
182 }
183 }
184
185 /**
186 * Maybe invalidate forum caches when a post is trashed/untrashed/deleted.
187 *
188 * @param int $post_id Post ID.
189 */
190 public function maybe_invalidate_forum_caches( $post_id ) {
191 if ( 'topic' === get_post_type( $post_id ) ) {
192 $this->invalidate_forum_caches( $post_id );
193 }
194 }
195
196 /**
197 * Tag Name
198 *
199 * @return void
200 */
201 public static function forum_topics_open_close(): void
202 {
203 $is_queried_obj = is_singular('forum') ? get_queried_object_id() : false;
204 $user_id = is_singular('forum') ? false : bbp_get_displayed_user_field('ID');
205
206 // Get the active tab from the URL or default to 'open'
207 $active_tab = isset($_GET['tab']) && in_array($_GET['tab'], ['open', 'closed'], true) ? sanitize_text_field($_GET['tab']) : 'open';
208
209 // Count open and closed topics efficiently
210 $open_count = forumax_get_topic_count_by_status('publish', $is_queried_obj);
211 $closed_count = forumax_get_topic_count_by_status('closed', $is_queried_obj);
212
213 if ( in_array('bbp-user-page', get_body_class(), true) ) :
214 ?>
215 <ul class="support-total-info">
216 <li class="open-ticket">
217 <?php esc_html_e( 'TOPIC', 'forumax' ); ?>
218 </li>
219 </ul>
220 <ul class="category-menu">
221 <li> <?php esc_html_e( 'Comments', 'forumax' ); ?> </li>
222 <li> <?php esc_html_e( 'Favorites', 'forumax' ); ?> </li>
223 </ul>
224 <?php
225 else :
226 ?>
227 <ul class="support-total-info">
228 <li class="open-ticket <?php echo 'open' === $active_tab ? 'active' : ''; ?>">
229 <i class="icon_info_alt"></i>
230 <a href="?tab=open"
231 class="open-data"
232 data-nonce="<?php echo wp_create_nonce( 'forumax-nonce' );?>"
233 data-type="open"
234 data-userid="<?php echo esc_attr($user_id); ?>"
235 data-parent="<?php echo esc_attr($is_queried_obj); ?>">
236 <?php echo esc_html($open_count) . esc_html__(' Open', 'forumax'); ?>
237 </a>
238 </li>
239 <li class="close-ticket <?php echo 'closed' === $active_tab ? 'active' : ''; ?>">
240 <i class="icon_check"></i>
241 <a href="?tab=closed"
242 class="open-data"
243 data-nonce="<?php echo wp_create_nonce( 'forumax-nonce' );?>"
244 data-type="closed"
245 data-parent="<?php echo esc_attr($is_queried_obj); ?>"
246 data-userid="<?php echo esc_attr($user_id); ?>">
247 <?php echo esc_html($closed_count) . esc_html__(' Closed', 'forumax'); ?>
248 </a>
249 </li>
250 </ul>
251 <?php
252 endif;
253 }
254
255 function topic_badges() {
256 if( bbp_is_topic_sticky(get_the_ID()) ) { ?>
257 <span class="badge badge-success">
258 <?php esc_html_e( 'Sticky', 'forumax' ); ?>
259 </span>
260 <?php }
261 if ( bbp_is_topic_closed( get_the_ID() ) ) { ?>
262 <span class="badge badge-danger">
263 <?php esc_html_e( 'Closed', 'forumax' ); ?>
264 </span>
265 <?php }
266 }
267
268 /**
269 * Inject sticky topics into the main topic loop
270 *
271 * @param array $posts The array of retrieved posts.
272 * @param WP_Query $query The WP_Query instance.
273 */
274 public function inject_sticky_topics( $posts, $query ) {
275 if ( is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
276 return $posts;
277 }
278
279 // Identify the target query: Topics ordered by ID (as in loop-topics.php )
280 if ( $query->get( 'post_type' ) !== 'topic' || $query->get( 'orderby' ) !== 'ID' ) {
281 return $posts;
282 }
283
284 // Only on first page
285 if ( $query->get( 'paged' ) > 1 ) {
286 return $posts;
287 }
288
289 // Prevent recursion
290 remove_filter( 'the_posts', [$this, 'inject_sticky_topics'], 10 );
291
292 $sticky_ids = [];
293
294 // Get Super Stickies
295 $super_stickies = bbp_get_super_stickies();
296
297 // Get Forum Stickies
298 $forum_id = 0;
299 if ( is_singular( 'forum' ) ) {
300 $forum_id = get_queried_object_id();
301 } elseif ( $query->get( 'post_parent' ) ) {
302 $post_parent = $query->get( 'post_parent' );
303 if ( is_numeric( $post_parent ) && $post_parent > 0 ) {
304 $forum_id = $post_parent;
305 }
306 }
307
308 $stickies = bbp_get_stickies( $forum_id );
309
310 $sticky_ids = array_unique(array_merge( $super_stickies, $stickies ) );
311
312 if ( ! empty( $sticky_ids ) ) {
313 $sticky_args = [
314 'post_type' => 'topic',
315 'post_parent' => 'any',
316 'post__in' => $sticky_ids,
317 'posts_per_page' => -1,
318 'orderby' => 'post__in',
319 'ignore_sticky_posts' => 1,
320 'no_found_rows' => true,
321 ];
322
323 $sticky_query = new WP_Query( $sticky_args );
324
325 if ( $sticky_query->have_posts() ) {
326 $sticky_posts = $sticky_query->posts;
327
328 // Filter out stickies from the main posts to avoid duplicates
329 $posts = array_filter( $posts, function ( $p ) use ( $sticky_ids ) {
330 return !in_array( $p->ID, $sticky_ids, true );
331 });
332
333 // Prepend stickies
334 $posts = array_merge( $sticky_posts, $posts );
335 }
336 }
337
338 add_filter( 'the_posts', [ $this, 'inject_sticky_topics'], 10, 2 );
339
340 return $posts;
341 }
342 }
343 }
344
345 /**
346 * Get Forumax Forum Class instance
347 * Works with or without Forumax theme active
348 *
349 * @return Forumax_Forum_Class|null
350 */
351 if ( ! function_exists( 'Forumax_Forum' ) ) {
352 function Forumax_Forum() {
353 if ( class_exists( 'Forumax_Forum_Class' ) ) {
354 return Forumax_Forum_Class::instance();
355 }
356 return null;
357 }
358 }
359