PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.5.1
Forumax – AI Powered Advanced Community Forum Plugin v2.5.1
2.5.1 2.5.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 All 31 releases
bbp-core / includes / classes / Forumax_Forum_Class.php

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

341 lines 13.6 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 // One grouped query instead of loading every site user and
56 // counting their topics one by one.
57 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
58 $results = $wpdb->get_results( $wpdb->prepare( "
59 SELECT post_author, COUNT(*) AS count
60 FROM $wpdb->posts
61 WHERE post_type = 'topic'
62 AND post_status = 'publish'
63 AND post_parent = %d
64 AND post_author > 0
65 GROUP BY post_author
66 ORDER BY count DESC
67 LIMIT 200
68 ", $forum_id ) );
69
70 $display_authors = [];
71 foreach ( (array) $results as $row ) {
72 $display_authors[] = [
73 'ID' => (int) $row->post_author,
74 'count' => (int) $row->count,
75 ];
76 }
77
78 set_transient( $cache_key, $display_authors, HOUR_IN_SECONDS );
79 }
80
81 if ( ! empty( $display_authors ) ) {
82 // Prime user caches for the whole list in one query.
83 cache_users( wp_list_pluck( $display_authors, 'ID' ) );
84
85 foreach ( $display_authors as $author_data ) {
86 $user_id = $author_data['ID'];
87 $user = get_userdata( $user_id );
88
89 if ( ! $user ) {
90 continue;
91 }
92
93 $post_count = $author_data['count'];
94
95 echo '<div class="userlist">';
96 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 ) . '">
97 <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>
98 </a>';
99 echo '</div>';
100 }
101 }
102 }
103
104 /**
105 * Tag Name
106 *
107 * @return string
108 */
109 public static function forum_topics_tags() {
110 global $wpdb;
111
112 $forum_id = get_queried_object_id();
113 $cache_key = 'frmx_tags_data_' . $forum_id;
114 $tags = get_transient( $cache_key );
115
116 if ( false === $tags ) {
117 $tags = $wpdb->get_results( $wpdb->prepare(
118 "SELECT DISTINCT t.term_id, t.name, t.slug, tt.count
119 FROM {$wpdb->terms} AS t
120 INNER JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id
121 INNER JOIN {$wpdb->term_relationships} AS tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
122 INNER JOIN {$wpdb->posts} AS p ON tr.object_id = p.ID
123 WHERE tt.taxonomy = 'topic-tag'
124 AND p.post_type = 'topic'
125 AND p.post_parent = %d
126 AND p.post_status = 'publish'
127 ORDER BY t.name ASC",
128 $forum_id
129 ) );
130
131 set_transient( $cache_key, $tags, HOUR_IN_SECONDS );
132 }
133
134 if ( $tags ) {
135 foreach ( $tags as $tag ) {
136 $post_count = $tag->count;
137 echo '<div class="tagList" id="' . esc_attr( $tag->slug ) . '">';
138 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>';
139 echo '</div>';
140 }
141 }
142 }
143
144 /**
145 * Invalidate forum caches when a topic is updated.
146 *
147 * @param int $topic_id Topic ID.
148 * @param int $forum_id Optional. Forum ID.
149 */
150 public function invalidate_forum_caches( $topic_id, $forum_id = 0 ) {
151 if ( ! $forum_id ) {
152 $forum_id = bbp_get_topic_forum_id( $topic_id );
153 }
154
155 if ( $forum_id ) {
156 delete_transient( 'frmx_authors_data_' . $forum_id );
157 delete_transient( 'frmx_tags_data_' . $forum_id );
158 delete_transient( 'frmx_topic_count_publish_' . $forum_id );
159 delete_transient( 'frmx_topic_count_closed_' . $forum_id );
160
161 // Also invalidate global topic counts (forum_id = 0) used on main archive
162 delete_transient( 'frmx_topic_count_publish_0' );
163 delete_transient( 'frmx_topic_count_closed_0' );
164 }
165 }
166
167 /**
168 * Maybe invalidate forum caches when a post is trashed/untrashed/deleted.
169 *
170 * @param int $post_id Post ID.
171 */
172 public function maybe_invalidate_forum_caches( $post_id ) {
173 if ( 'topic' === get_post_type( $post_id ) ) {
174 $this->invalidate_forum_caches( $post_id );
175 }
176 }
177
178 /**
179 * Tag Name
180 *
181 * @return void
182 */
183 public static function forum_topics_open_close(): void
184 {
185 $is_queried_obj = is_singular('forum') ? get_queried_object_id() : false;
186 $user_id = is_singular('forum') ? false : bbp_get_displayed_user_field('ID');
187
188 // Get the active tab from the URL or default to 'open'
189 $active_tab = isset($_GET['tab']) && in_array($_GET['tab'], ['open', 'closed'], true) ? sanitize_text_field($_GET['tab']) : 'open';
190
191 // Count open and closed topics efficiently
192 $open_count = forumax_get_topic_count_by_status('publish', $is_queried_obj);
193 $closed_count = forumax_get_topic_count_by_status('closed', $is_queried_obj);
194
195 if ( forumax_is_user_profile_page() ) :
196 ?>
197 <ul class="support-total-info">
198 <li class="open-ticket">
199 <?php esc_html_e( 'TOPIC', 'forumax' ); ?>
200 </li>
201 </ul>
202 <ul class="category-menu">
203 <li> <?php esc_html_e( 'Comments', 'forumax' ); ?> </li>
204 <li> <?php esc_html_e( 'Favorites', 'forumax' ); ?> </li>
205 </ul>
206 <?php
207 else :
208 ?>
209 <ul class="support-total-info">
210 <li class="open-ticket <?php echo 'open' === $active_tab ? 'active' : ''; ?>">
211 <i class="icon_info_alt"></i>
212 <a href="?tab=open"
213 class="open-data"
214 data-nonce="<?php echo wp_create_nonce( 'forumax-nonce' );?>"
215 data-type="open"
216 data-userid="<?php echo esc_attr($user_id); ?>"
217 data-parent="<?php echo esc_attr($is_queried_obj); ?>">
218 <?php echo esc_html($open_count) . esc_html__(' Open', 'forumax'); ?>
219 </a>
220 </li>
221 <li class="close-ticket <?php echo 'closed' === $active_tab ? 'active' : ''; ?>">
222 <i class="icon_check"></i>
223 <a href="?tab=closed"
224 class="open-data"
225 data-nonce="<?php echo wp_create_nonce( 'forumax-nonce' );?>"
226 data-type="closed"
227 data-parent="<?php echo esc_attr($is_queried_obj); ?>"
228 data-userid="<?php echo esc_attr($user_id); ?>">
229 <?php echo esc_html($closed_count) . esc_html__(' Closed', 'forumax'); ?>
230 </a>
231 </li>
232 </ul>
233 <?php
234 endif;
235 }
236
237 function topic_badges() {
238 if( bbp_is_topic_sticky(get_the_ID()) ) { ?>
239 <span class="badge badge-success">
240 <?php esc_html_e( 'Sticky', 'forumax' ); ?>
241 </span>
242 <?php }
243 if ( bbp_is_topic_closed( get_the_ID() ) ) { ?>
244 <span class="badge badge-danger">
245 <?php esc_html_e( 'Closed', 'forumax' ); ?>
246 </span>
247 <?php }
248 }
249
250 /**
251 * Inject sticky topics into the main topic loop
252 *
253 * @param array $posts The array of retrieved posts.
254 * @param WP_Query $query The WP_Query instance.
255 */
256 public function inject_sticky_topics( $posts, $query ) {
257 if ( is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
258 return $posts;
259 }
260
261 // Identify the target query: Topics ordered by ID (as in loop-topics.php )
262 if ( $query->get( 'post_type' ) !== 'topic' || $query->get( 'orderby' ) !== 'ID' ) {
263 return $posts;
264 }
265
266 // Only on first page
267 if ( $query->get( 'paged' ) > 1 ) {
268 return $posts;
269 }
270
271 // Prevent recursion
272 remove_filter( 'the_posts', [$this, 'inject_sticky_topics'], 10 );
273
274 $sticky_ids = [];
275
276 // Get Super Stickies
277 $super_stickies = bbp_get_super_stickies();
278
279 // Get Forum Stickies
280 $forum_id = 0;
281 if ( is_singular( 'forum' ) ) {
282 $forum_id = get_queried_object_id();
283 } elseif ( $query->get( 'post_parent' ) ) {
284 $post_parent = $query->get( 'post_parent' );
285 if ( is_numeric( $post_parent ) && $post_parent > 0 ) {
286 $forum_id = $post_parent;
287 }
288 }
289
290 $stickies = bbp_get_stickies( $forum_id );
291
292 $sticky_ids = array_unique(array_merge( $super_stickies, $stickies ) );
293
294 if ( ! empty( $sticky_ids ) ) {
295 $sticky_args = [
296 'post_type' => 'topic',
297 'post_parent' => 'any',
298 'post__in' => $sticky_ids,
299 'posts_per_page' => -1,
300 'orderby' => 'post__in',
301 'ignore_sticky_posts' => 1,
302 'no_found_rows' => true,
303 ];
304
305 $sticky_query = new WP_Query( $sticky_args );
306
307 if ( $sticky_query->have_posts() ) {
308 $sticky_posts = $sticky_query->posts;
309
310 // Filter out stickies from the main posts to avoid duplicates
311 $posts = array_filter( $posts, function ( $p ) use ( $sticky_ids ) {
312 return !in_array( $p->ID, $sticky_ids, true );
313 });
314
315 // Prepend stickies
316 $posts = array_merge( $sticky_posts, $posts );
317 }
318 }
319
320 add_filter( 'the_posts', [ $this, 'inject_sticky_topics'], 10, 2 );
321
322 return $posts;
323 }
324 }
325 }
326
327 /**
328 * Get Forumax Forum Class instance
329 * Works with or without Forumax theme active
330 *
331 * @return Forumax_Forum_Class|null
332 */
333 if ( ! function_exists( 'Forumax_Forum' ) ) {
334 function Forumax_Forum() {
335 if ( class_exists( 'Forumax_Forum_Class' ) ) {
336 return Forumax_Forum_Class::instance();
337 }
338 return null;
339 }
340 }
341