PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.4.3
Forumax – AI Powered Advanced Community Forum Plugin v2.4.3
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 / features / bbp_voting / frontend.php

frontend.php in Forumax – AI Powered Advanced Community Forum Plugin 2.4.3, at includes/features/bbp_voting/frontend.php

367 lines 11.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit; // Exit if accessed directly.
4 }
5
6 // Vote Buttons and Score hooks.
7 add_action( 'bbp_theme_after_topic_content', 'bbp_voting_buttons' );
8 add_action( 'forumax_reply_avatar_voting', 'bbp_voting_buttons' );
9 add_action( 'bbp_voting_cpt', 'bbp_voting_buttons', 10, 1 );
10
11 function bbp_voting_buttons( $post_obj = false ) {
12 // Don't show voting buttons on user profile pages
13 if ( function_exists( 'bbp_is_single_user' ) && bbp_is_single_user() ) {
14 return;
15 }
16
17 $current_action = current_action();
18 $topic_post_type = bbp_get_topic_post_type();
19 $reply_post_type = bbp_get_reply_post_type();
20 $this_post_type = '';
21 $post = null;
22
23 // Prefer explicitly passed post object (custom hooks/CPT usage).
24 if ( $post_obj instanceof WP_Post ) {
25 $post = $post_obj;
26 $this_post_type = get_post_type( $post->ID );
27 }
28
29 // Fallback to action-based context detection.
30 if ( empty( $post ) ) {
31 if ( $current_action === 'bbp_voting_cpt' ) {
32 if ( ! $post_obj ) return;
33 $post = $post_obj;
34 $this_post_type = get_post_type( $post->ID );
35 } else {
36 // Determine post type from current action
37 if ( in_array( $current_action, [
38 'bbp_theme_before_topic_title',
39 'bbp_template_before_lead_topic',
40 'bbp_theme_after_topic_author_details',
41 'bbp_theme_after_topic_content',
42 ] ) ) {
43 $this_post_type = $topic_post_type;
44 $post = bbpress()->topic_query->post;
45 } elseif ( in_array( $current_action, [
46 'bbp_theme_before_reply_content',
47 'bbp_theme_after_reply_author_details',
48 'forumax_reply_avatar_voting',
49 'bbp_theme_after_reply_content',
50 ] ) ) {
51 $this_post_type = bbp_voting_get_current_post_type();
52 $post = bbpress()->reply_query->post;
53 }
54 }
55 }
56
57 // Exit if unknown context or no post
58 if ( empty( $this_post_type ) || empty( $post ) ) return;
59
60 // Check forum settings
61 $forum_id = ($this_post_type === $topic_post_type)
62 ? bbp_get_topic_forum_id( $post->ID )
63 : bbp_get_reply_forum_id( $post->ID );
64
65 // Check if voting is allowed on this forum
66 if ( ! apply_filters( 'bbp_voting_allowed_on_forum', true, $forum_id ) ) return;
67
68 // Get post-specific settings
69 $post_setting = ($this_post_type === $topic_post_type)
70 ? get_post_meta( $forum_id, 'bbp_voting_forum_enable_topics', true )
71 : get_post_meta( $forum_id, 'bbp_voting_forum_enable_replies', true );
72
73 $broad_disable = ($this_post_type === $topic_post_type)
74 ? forumax_get_opt( 'is_voting_disabled_topics', 0 )
75 : forumax_get_opt( 'is_voting_disabled_replies', 0 );
76
77 // Check if voting is disabled
78 if ( ! empty( $post_setting ) && $post_setting === 'false' ) return;
79 if ( empty( $post_setting ) && $broad_disable === '0' ) return;
80
81 $post_id = $post->ID;
82
83 $score = (int) get_post_meta( $post_id, 'bbp_voting_score', true );
84 $ups = (int) get_post_meta( $post_id, 'bbp_voting_ups', true );
85 $downs = (int) get_post_meta( $post_id, 'bbp_voting_downs', true );
86
87 $calc_score = $ups + $downs;
88 if ( $score > $calc_score ) {
89 $diff = $score - $calc_score;
90 $ups += $diff;
91 update_post_meta( $post_id, 'bbp_voting_ups', $ups );
92 }
93
94 $voting_log = get_post_meta( $post_id, 'bbp_voting_log', true );
95 $voting_log = is_array( $voting_log ) ? $voting_log : [];
96 $client_ip = $_SERVER['REMOTE_ADDR'];
97 $identifier = is_user_logged_in() ? get_current_user_id() : $client_ip;
98 $existing_vote = $voting_log[ $identifier ] ?? 0;
99
100 // Check if admin can bypass voting restrictions
101 $admin_bypass = current_user_can( 'administrator' ) &&
102 forumax_get_opt( 'is_admin_can_vote_unlimited', false );
103
104 // Determine if this is view-only mode
105 $view_only = ! is_user_logged_in() &&
106 forumax_get_opt( 'is_disabled_voting_for_non_logged_users', false );
107
108 // Additional view-only checks for regular context (not CPT)
109 if ( ! $view_only && $current_action !== 'bbp_voting_cpt' ) {
110 // Check if topic is closed and voting on closed topics is disabled
111 $topic_id = ( $this_post_type === $topic_post_type ) ?
112 $post_id : bbp_get_reply_topic_id( $post_id );
113
114 if ( get_post_status( $topic_id ) === 'closed' &&
115 forumax_get_opt( 'is_disabled_voting_closed_topics', false ) ) {
116 $view_only = true;
117 }
118
119 // Check if voting on own posts is disabled
120 if ( ! $view_only &&
121 forumax_get_opt( 'is_disabled_voting_own_topic_reply', false ) &&
122 $post->post_author == get_current_user_id() ) {
123 $view_only = true;
124 }
125 }
126
127 $disable_down = forumax_get_opt( 'is_down_votes_disabled', 0 );
128 $vote_number_display = forumax_get_opt( 'vote_numbers_display', 'hover' );
129 $display_vote_nums = 'num-' . $vote_number_display;
130
131 // Determine if floating style should be applied
132 $float = ( $current_action === 'bbp_voting_cpt' );
133
134 // Build CSS classes for the voting container
135 $vote_classes = [
136 'bbp-voting',
137 'bbp-voting-post-' . $post_id,
138 get_post_type( $post_id )
139 ];
140
141 if ( $view_only ) {
142 $vote_classes[] = 'view-only';
143 } elseif ( $existing_vote == 1 ) {
144 $vote_classes[] = 'voted-up';
145 } elseif ( $existing_vote == -1 ) {
146 $vote_classes[] = 'voted-down';
147 }
148
149 if ( $admin_bypass ) $vote_classes[] = 'admin-bypass';
150 if ( $float ) $vote_classes[] = 'bbp-voting-float';
151
152 // Start building HTML
153 $html = '<div class="' . implode(' ', $vote_classes) . '">';
154
155 // Add label if enabled
156 $is_label = forumax_get_opt( 'is_label', true );
157 if ( $is_label ) {
158 $upvote_label = forumax_get_opt( 'upvote_label' );
159 if ( $upvote_label ) {
160 $html .= '<div class="bbp-voting-label helpful">' . esc_html( $upvote_label ) . '</div>';
161 }
162 }
163
164 // Generate voting HTML based on whether we're in AMP mode or not
165 if ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) {
166 // AMP version of voting buttons
167 $post_url = admin_url( 'admin-ajax.php' );
168 $plusups = $ups ? '+' . $ups : ' ';
169
170 // Upvote form
171 $html .= sprintf(
172 '<form name="amp-form%1$s" method="post" action-xhr="%2$s" target="_top" on="submit-success: AMP.setState({\'voteup%1$s\': %3$s})">
173 <input type="hidden" name="action" value="bbpress_post_vote_link_clicked">
174 <input type="hidden" name="post_id" value="%4$s" />
175 <input type="hidden" name="direction" value="1" />
176 <input type="submit" class="nobutton upvote-amp" value="🔺" />
177 <span class="vote up" [text]="voteup%1$s ? \'+\' + voteup%1$s : \'%5$s\'">%5$s</span>
178 </form>',
179 $post_id,
180 esc_url( $post_url ),
181 ( $ups + 1 ),
182 esc_attr( $post_id ),
183 esc_html( $plusups )
184 );
185
186
187 // Downvote form (if enabled)
188 if ( $disable_down == 1 ) {
189 $html .= sprintf(
190 '<form name="amp-form%1$s" method="post" action-xhr="%2$s" target="_top" on="submit-success: AMP.setState({\'votedown%1$s\': %3$s})">
191 <input type="hidden" name="action" value="bbpress_post_vote_link_clicked">
192 <input type="hidden" name="post_id" value="%4$s" />
193 <input type="hidden" name="direction" value="-1" />
194 <input type="submit" class="nobutton downvote-amp" value="🔻" />
195 <span class="vote down" [text]="votedown%1$s || \'%5$s\'">%6$s</span>
196 </form>',
197 $post_id,
198 esc_url( $post_url ),
199 ( $downs - 1 ),
200 esc_attr( $post_id ),
201 esc_html( $downs ? $downs : ' ' ),
202 esc_html( $downs ? $downs : '' )
203 );
204 }
205 } else {
206 // Standard version of voting buttons
207 $html .= '<a class="vote up ' . esc_attr( $display_vote_nums ) . '" data-votes="' .
208 ( $ups ? '+' . $ups : '' ) . '" onclick="bbpress_post_vote_link_clicked(' .
209 $post_id . ', 1); return false;">Up</a>';
210
211 $html .= '<div class="score">' . $score . '</div>';
212
213 if ( $disable_down == 1 ) {
214 $html .= '<a class="vote down ' . esc_attr( $display_vote_nums ) . '" data-votes="' .
215 ( $downs ? $downs : '' ) . '" onclick="bbpress_post_vote_link_clicked(' .
216 $post_id . ', -1); return false;">Down</a>';
217 }
218 }
219
220 if ( $disable_down == 1 && $is_label ) {
221 $downvote_label = forumax_get_opt( 'downvote_label' );
222 if ( $downvote_label ) {
223 $html .= '<div class="bbp-voting-label not-helpful">' . esc_html( $downvote_label ) . '</div>';
224 }
225 }
226
227 // Apply filter for reply voting buttons
228 if ( $this_post_type === $reply_post_type ) {
229 $html = apply_filters( 'bbp_voting_after_reply_voting_buttons', $html, $post_id );
230 }
231
232 // Close the voting container
233 $html .= '</div>';
234
235 echo $html;
236 }
237
238
239 // Sort by Votes
240
241 add_filter( 'bbp_has_topics_query', 'sort_bbpress_posts_by_votes', 99 );
242 add_filter( 'bbp_has_replies_query', 'sort_bbpress_posts_by_votes', 99 );
243
244 function sort_bbpress_posts_by_votes( $args = [] ) {
245 $forum_id = bbp_get_forum_id();
246 $forum_post_type = bbp_get_forum_post_type();
247 $topic_post_type = bbp_get_topic_post_type();
248
249 // Determine post type and settings based on current filter
250 $current_filter = current_filter();
251 switch ( $current_filter ) {
252 case 'bbp_has_topics_query':
253 $this_post_type = $forum_post_type;
254 $post_setting = get_post_meta( $forum_id, 'sort_bbpress_topics_by_votes_on_forum', true );
255 $broad_enable = forumax_get_opt( 'is_sort_topic_by_votes', false );
256 break;
257 case 'bbp_has_replies_query':
258 $this_post_type = $topic_post_type;
259 $post_setting = get_post_meta( $forum_id, 'sort_bbpress_replies_by_votes_on_forum', true );
260 $broad_enable = forumax_get_opt( 'is_sort_reply_by_votes', false );
261 break;
262 default:
263 return $args;
264 }
265
266 // Check if we should apply vote sorting
267 $apply_sorting = false;
268
269 // Check URL parameter first
270 if ( isset( $_GET['bbp-voting-sort'] ) ) {
271 if ( $_GET['bbp-voting-sort'] === 'best' ) {
272 $apply_sorting = true;
273 } elseif ( $_GET['bbp-voting-sort'] === 'default' || $_GET['bbp-voting-sort'] === '' ) {
274 return $args;
275 }
276 } else {
277 // Check forum settings
278 if ( ! empty( $post_setting ) ) {
279 if ( $post_setting !== 'false' ) {
280 $apply_sorting = true;
281 }
282 } elseif ( $broad_enable !== '1' ) {
283 $apply_sorting = true;
284 }
285
286 // Check if voting is allowed on this forum
287 if ( ! apply_filters( 'bbp_voting_allowed_on_forum', true, $forum_id ) ) {
288 $apply_sorting = false;
289 }
290 }
291
292 // Return original args if sorting should not be applied
293 if ( ! $apply_sorting ) {
294 return $args;
295 }
296
297 // Get the meta key to sort by
298 $sort_meta_key = apply_filters( 'bbp_voting_sort_meta_key', 'bbp_voting_score' );
299
300 // Find and fill any posts missing the sort meta key
301 $cache_key = 'frmx_vote_backfill_' . md5( serialize( $args ) );
302 if ( false === get_transient( $cache_key ) ) {
303 $missing_meta_query = [
304 'meta_query' => [
305 [
306 'key' => $sort_meta_key,
307 'compare' => 'NOT EXISTS',
308 'value' => '',
309 ],
310 ],
311 'fields' => 'ids',
312 ];
313
314 $query = new WP_Query( array_merge( $args, $missing_meta_query ) );
315 foreach ( $query->posts as $post_id ) {
316 $default_value = apply_filters( 'bbp_voting_sort_meta_key_default_value', '0', $post_id );
317 update_post_meta( $post_id, $sort_meta_key, $default_value );
318 }
319
320 // Cache for 24 hours to skip subsequent checks
321 set_transient( $cache_key, 'done', DAY_IN_SECONDS );
322 }
323
324 // Clear existing sort parameters
325 $args = array_diff_key( $args, array_flip( ['meta_key', 'meta_type', 'orderby', 'order'] ) );
326
327 // Set up meta query for sorting
328 $args['meta_query'] = [
329 'relation' => 'AND',
330 'score_clause' => [
331 'key' => $sort_meta_key,
332 'compare' => 'EXISTS',
333 ],
334 ];
335
336 // Set numeric type for score meta if using the default key
337 if ( $sort_meta_key === 'bbp_voting_score' ) {
338 $args['meta_query']['score_clause']['type'] = 'NUMERIC';
339 }
340
341 // Set up orderby parameters
342 $args['orderby'] = [
343 'post_type' => 'DESC',
344 'score_clause' => 'DESC',
345 ];
346
347 // Add additional sorting parameters based on post type
348 if ( $this_post_type === $topic_post_type ) {
349 $args['orderby']['date'] = 'ASC';
350 } elseif ( $this_post_type === $forum_post_type ) {
351 $args['meta_query']['orderby_freshness'] = [
352 'key' => '_bbp_last_active_time',
353 'type' => 'DATETIME',
354 ];
355 $args['orderby']['orderby_freshness'] = 'DESC';
356 }
357
358 return $args;
359 }
360
361 add_action( 'init', 'bbp_voting_lead_topic' );
362 function bbp_voting_lead_topic() {
363 if ( forumax_get_opt( 'is_lead_topic_broken', false ) ) {
364 add_filter( 'bbp_show_lead_topic', '__return_true' );
365 }
366 }
367