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 / features / bbp_voting / frontend.php

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

358 lines 11.6 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 // Match the identifier the AJAX handler stores (hashed IP for visitors),
97 // falling back to legacy raw-IP entries saved before 2.3.1.
98 $existing_vote = 0;
99 if ( is_user_logged_in() ) {
100 $existing_vote = $voting_log[ get_current_user_id() ] ?? 0;
101 } elseif ( ! empty( $voting_log ) ) {
102 $existing_vote = $voting_log[ forumax_get_hashed_ip() ] ?? 0;
103 if ( ! $existing_vote ) {
104 $raw_ip = forumax_get_raw_ip();
105 $existing_vote = $raw_ip ? ( $voting_log[ $raw_ip ] ?? 0 ) : 0;
106 }
107 }
108
109 // Check if admin can bypass voting restrictions
110 $admin_bypass = current_user_can( 'administrator' ) &&
111 forumax_get_opt( 'is_admin_can_vote_unlimited', false );
112
113 // Determine if this is view-only mode
114 $view_only = ! is_user_logged_in() &&
115 forumax_get_opt( 'is_disabled_voting_for_non_logged_users', false );
116
117 // Additional view-only checks for regular context (not CPT)
118 if ( ! $view_only && $current_action !== 'bbp_voting_cpt' ) {
119 // Check if topic is closed and voting on closed topics is disabled
120 $topic_id = ( $this_post_type === $topic_post_type ) ?
121 $post_id : bbp_get_reply_topic_id( $post_id );
122
123 if ( get_post_status( $topic_id ) === 'closed' &&
124 forumax_get_opt( 'is_disabled_voting_closed_topics', false ) ) {
125 $view_only = true;
126 }
127
128 // Check if voting on own posts is disabled
129 if ( ! $view_only &&
130 forumax_get_opt( 'is_disabled_voting_own_topic_reply', false ) &&
131 $post->post_author == get_current_user_id() ) {
132 $view_only = true;
133 }
134 }
135
136 $disable_down = forumax_get_opt( 'is_down_votes_disabled', 0 );
137 $vote_number_display = forumax_get_opt( 'vote_numbers_display', 'hover' );
138 $display_vote_nums = 'num-' . $vote_number_display;
139
140 // Determine if floating style should be applied
141 $float = ( $current_action === 'bbp_voting_cpt' );
142
143 // Build CSS classes for the voting container
144 $vote_classes = [
145 'bbp-voting',
146 'bbp-voting-post-' . $post_id,
147 get_post_type( $post_id )
148 ];
149
150 if ( $view_only ) {
151 $vote_classes[] = 'view-only';
152 } elseif ( $existing_vote == 1 ) {
153 $vote_classes[] = 'voted-up';
154 } elseif ( $existing_vote == -1 ) {
155 $vote_classes[] = 'voted-down';
156 }
157
158 if ( $admin_bypass ) $vote_classes[] = 'admin-bypass';
159 if ( $float ) $vote_classes[] = 'bbp-voting-float';
160
161 // Start building HTML
162 $html = '<div class="' . implode(' ', $vote_classes) . '">';
163
164 // Add label if enabled
165 $is_label = forumax_get_opt( 'is_label', true );
166 if ( $is_label ) {
167 $upvote_label = forumax_get_opt( 'upvote_label' );
168 if ( $upvote_label ) {
169 $html .= '<div class="bbp-voting-label helpful">' . esc_html( $upvote_label ) . '</div>';
170 }
171 }
172
173 // Generate voting HTML based on whether we're in AMP mode or not
174 if ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) {
175 // AMP version of voting buttons
176 $post_url = admin_url( 'admin-ajax.php' );
177 $plusups = $ups ? '+' . $ups : ' ';
178
179 // Upvote form
180 $html .= sprintf(
181 '<form name="amp-form%1$s" method="post" action-xhr="%2$s" target="_top" on="submit-success: AMP.setState({\'voteup%1$s\': %3$s})">
182 <input type="hidden" name="action" value="bbpress_post_vote_link_clicked">
183 <input type="hidden" name="post_id" value="%4$s" />
184 <input type="hidden" name="direction" value="1" />
185 <input type="submit" class="nobutton upvote-amp" value="🔺" />
186 <span class="vote up" [text]="voteup%1$s ? \'+\' + voteup%1$s : \'%5$s\'">%5$s</span>
187 </form>',
188 $post_id,
189 esc_url( $post_url ),
190 ( $ups + 1 ),
191 esc_attr( $post_id ),
192 esc_html( $plusups )
193 );
194
195
196 // Downvote form (if enabled)
197 if ( $disable_down == 1 ) {
198 $html .= sprintf(
199 '<form name="amp-form%1$s" method="post" action-xhr="%2$s" target="_top" on="submit-success: AMP.setState({\'votedown%1$s\': %3$s})">
200 <input type="hidden" name="action" value="bbpress_post_vote_link_clicked">
201 <input type="hidden" name="post_id" value="%4$s" />
202 <input type="hidden" name="direction" value="-1" />
203 <input type="submit" class="nobutton downvote-amp" value="🔻" />
204 <span class="vote down" [text]="votedown%1$s || \'%5$s\'">%6$s</span>
205 </form>',
206 $post_id,
207 esc_url( $post_url ),
208 ( $downs - 1 ),
209 esc_attr( $post_id ),
210 esc_html( $downs ? $downs : ' ' ),
211 esc_html( $downs ? $downs : '' )
212 );
213 }
214 } else {
215 // Standard version of voting buttons
216 $html .= '<a class="vote up ' . esc_attr( $display_vote_nums ) . '" data-votes="' .
217 ( $ups ? '+' . $ups : '' ) . '" onclick="bbpress_post_vote_link_clicked(' .
218 $post_id . ', 1); return false;">Up</a>';
219
220 $html .= '<div class="score">' . $score . '</div>';
221
222 if ( $disable_down == 1 ) {
223 $html .= '<a class="vote down ' . esc_attr( $display_vote_nums ) . '" data-votes="' .
224 ( $downs ? $downs : '' ) . '" onclick="bbpress_post_vote_link_clicked(' .
225 $post_id . ', -1); return false;">Down</a>';
226 }
227 }
228
229 if ( $disable_down == 1 && $is_label ) {
230 $downvote_label = forumax_get_opt( 'downvote_label' );
231 if ( $downvote_label ) {
232 $html .= '<div class="bbp-voting-label not-helpful">' . esc_html( $downvote_label ) . '</div>';
233 }
234 }
235
236 // Apply filter for reply voting buttons
237 if ( $this_post_type === $reply_post_type ) {
238 $html = apply_filters( 'bbp_voting_after_reply_voting_buttons', $html, $post_id );
239 }
240
241 // Close the voting container
242 $html .= '</div>';
243
244 echo $html;
245 }
246
247
248 // Sort by Votes
249
250 add_filter( 'bbp_has_topics_query', 'sort_bbpress_posts_by_votes', 99 );
251 add_filter( 'bbp_has_replies_query', 'sort_bbpress_posts_by_votes', 99 );
252
253 function sort_bbpress_posts_by_votes( $args = [] ) {
254 $forum_id = bbp_get_forum_id();
255 $forum_post_type = bbp_get_forum_post_type();
256 $topic_post_type = bbp_get_topic_post_type();
257
258 // Determine post type and settings based on current filter
259 $current_filter = current_filter();
260 switch ( $current_filter ) {
261 case 'bbp_has_topics_query':
262 $this_post_type = $forum_post_type;
263 $post_setting = get_post_meta( $forum_id, 'sort_bbpress_topics_by_votes_on_forum', true );
264 $broad_enable = forumax_get_opt( 'is_sort_topic_by_votes', false );
265 break;
266 case 'bbp_has_replies_query':
267 $this_post_type = $topic_post_type;
268 $post_setting = get_post_meta( $forum_id, 'sort_bbpress_replies_by_votes_on_forum', true );
269 $broad_enable = forumax_get_opt( 'is_sort_reply_by_votes', false );
270 break;
271 default:
272 return $args;
273 }
274
275 // Check if we should apply vote sorting
276 $apply_sorting = false;
277
278 // Check URL parameter first
279 if ( isset( $_GET['bbp-voting-sort'] ) ) {
280 if ( $_GET['bbp-voting-sort'] === 'best' ) {
281 $apply_sorting = true;
282 } elseif ( $_GET['bbp-voting-sort'] === 'default' || $_GET['bbp-voting-sort'] === '' ) {
283 return $args;
284 }
285 } else {
286 // Check forum settings
287 if ( ! empty( $post_setting ) ) {
288 if ( $post_setting !== 'false' ) {
289 $apply_sorting = true;
290 }
291 } elseif ( $broad_enable !== '1' ) {
292 $apply_sorting = true;
293 }
294
295 // Check if voting is allowed on this forum
296 if ( ! apply_filters( 'bbp_voting_allowed_on_forum', true, $forum_id ) ) {
297 $apply_sorting = false;
298 }
299 }
300
301 // Return original args if sorting should not be applied
302 if ( ! $apply_sorting ) {
303 return $args;
304 }
305
306 // Get the meta key to sort by
307 $sort_meta_key = apply_filters( 'bbp_voting_sort_meta_key', 'bbp_voting_score' );
308
309 // The default key is backfilled once site-wide and on save (see
310 // forumax_backfill_voting_score_meta_once()), not per page view.
311 if ( 'bbp_voting_score' !== $sort_meta_key ) {
312 forumax_backfill_voting_sort_meta( $sort_meta_key, $args );
313 }
314
315 // Clear existing sort parameters
316 $args = array_diff_key( $args, array_flip( ['meta_key', 'meta_type', 'orderby', 'order'] ) );
317
318 // Set up meta query for sorting
319 $args['meta_query'] = [
320 'relation' => 'AND',
321 'score_clause' => [
322 'key' => $sort_meta_key,
323 'compare' => 'EXISTS',
324 ],
325 ];
326
327 // Set numeric type for score meta if using the default key
328 if ( $sort_meta_key === 'bbp_voting_score' ) {
329 $args['meta_query']['score_clause']['type'] = 'NUMERIC';
330 }
331
332 // Set up orderby parameters
333 $args['orderby'] = [
334 'post_type' => 'DESC',
335 'score_clause' => 'DESC',
336 ];
337
338 // Add additional sorting parameters based on post type
339 if ( $this_post_type === $topic_post_type ) {
340 $args['orderby']['date'] = 'ASC';
341 } elseif ( $this_post_type === $forum_post_type ) {
342 $args['meta_query']['orderby_freshness'] = [
343 'key' => '_bbp_last_active_time',
344 'type' => 'DATETIME',
345 ];
346 $args['orderby']['orderby_freshness'] = 'DESC';
347 }
348
349 return $args;
350 }
351
352 add_action( 'init', 'bbp_voting_lead_topic' );
353 function bbp_voting_lead_topic() {
354 if ( forumax_get_opt( 'is_lead_topic_broken', false ) ) {
355 add_filter( 'bbp_show_lead_topic', '__return_true' );
356 }
357 }
358