PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.5.0
Forumax – AI Powered Advanced Community Forum Plugin v2.5.0
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 2.0.0 All 30 releases
← All changes | includes/features/bbp_voting/frontend.php +366 -348 1.2.5 → 2.5.0 View file →
@@ -1,348 +1,366 @@
1 -<?php
2 -if ( ! defined( 'ABSPATH' ) ) {
3 - exit; // Exit if accessed directly.
4 -}
5 -
6 -$opt = get_option( 'bbp_core_settings' );
7 -$voting_position = $opt['voting_position'] ?? 'below_user';
8 -
9 -
10 -// Vote Buttons and Score hooks.
11 -// add_action('bbp_theme_before_topic_author_details', 'bbp_voting_buttons');
12 -// add_action('bbp_theme_before_reply_author_details', 'bbp_voting_buttons');
13 -add_action( 'bbp_theme_before_topic_title', 'bbp_voting_buttons' );
14 -// add_action('bbp_template_before_lead_topic', 'bbp_voting_buttons');
15 -
16 -add_action( 'bbp_theme_before_topic_content', 'bbp_voting_buttons' );
17 -
18 -if ( 'before_content' == $voting_position ) {
19 - add_action( 'bbp_theme_before_reply_content', 'bbp_voting_buttons' );
20 -} elseif ( 'below_user' == $voting_position ) {
21 - add_action( 'bbp_theme_after_reply_author_details', 'bbp_voting_buttons' );
22 -}
23 -
24 -// add_filter('bbp_get_topic_author_link', 'bbp_voting_buttons', 9, 3);
25 -// add_filter('bbp_get_reply_author_link', 'bbp_voting_buttons', 9, 3);
26 -add_action( 'bbp_voting_cpt', 'bbp_voting_buttons', 10, 1 );
27 -
28 -function bbp_voting_buttons( $post_obj = false ) {
29 - $opt = get_option( 'bbp_core_settings' );
30 -
31 - $current_action = current_action();
32 -
33 - if ( $current_action === 'bbp_voting_cpt' ) {
34 -
35 - // Using a custom hook for a custom post type
36 - if ( ! $post_obj ) {
37 - return;
38 - }
39 -
40 - $post = $post_obj;
41 -
42 - } else {
43 - $topic_post_type = bbp_get_topic_post_type();
44 - $reply_post_type = bbp_get_reply_post_type();
45 -
46 - if ( in_array( $current_action, [ 'bbp_theme_before_topic_title', 'bbp_template_before_lead_topic', 'bbp_theme_before_topic_content' ] ) ) {
47 -
48 - $this_post_type = $topic_post_type;
49 - }
50 -
51 - if ( in_array( $current_action, [ 'bbp_theme_before_reply_content', 'bbp_theme_after_reply_author_details' ] ) ) {
52 - $this_post_type = bbp_voting_get_current_post_type();
53 - }
54 -
55 - // Get the post.
56 - if ( $this_post_type == $topic_post_type ) {
57 - $post = bbpress()->topic_query->post;
58 - }
59 -
60 - if ( $this_post_type == $reply_post_type ) {
61 - $post = bbpress()->reply_query->post;
62 - }
63 - }
64 -
65 - // Do we have a post?
66 - if ( ! empty( $post ) ) {
67 - $post_id = $post->ID;
68 - // Since we're using a filter on the author link, avoid duplicates
69 - // if($bbp_voting_last_author_link_post_id === $post_id) {
70 - // Duplicate
71 - // return $author_link;
72 - // } else {
73 - // New, continue, but set the global variable
74 - // $bbp_voting_last_author_link_post_id = $post_id;
75 - // }
76 - if ( $current_action === 'bbp_voting_cpt' ) {
77 - $post_setting = true;
78 - $broad_disable = false;
79 - } else {
80 - switch ( $this_post_type ) {
81 - case $topic_post_type:
82 - $forum_id = bbp_get_topic_forum_id( $post_id );
83 - $post_setting = get_post_meta( $forum_id, 'bbp_voting_forum_enable_topics', true );
84 - $broad_disable = $opt['is_voting_disabled_topics'] ?? 0;
85 - break;
86 - case $reply_post_type:
87 - $forum_id = bbp_get_reply_forum_id( $post_id );
88 - $post_setting = get_post_meta( $forum_id, 'bbp_voting_forum_enable_replies', true );
89 - $broad_disable = $opt['is_voting_disabled_replies'] ?? 0;
90 - break;
91 - }
92 - // Filter Hook: 'bbp_voting_allowed_on_forum'
93 - if ( ! apply_filters( 'bbp_voting_allowed_on_forum', true, $forum_id ) ) {
94 - return;
95 - }
96 - }
97 - if ( ! empty( $post_setting ) ) {
98 - // Forum-specific override is set (not Default)
99 - if ( $post_setting === 'false' ) {
100 - return;
101 - }
102 - } else {
103 - // Use broad disable settings
104 - if ( $broad_disable === '0' ) {
105 - return;
106 - }
107 - }
108 - // Done with "allowed" checks... let's do this
109 - $score = (int) get_post_meta( $post_id, 'bbp_voting_score', true );
110 - $ups = (int) get_post_meta( $post_id, 'bbp_voting_ups', true );
111 - $downs = (int) get_post_meta( $post_id, 'bbp_voting_downs', true );
112 - // Check for, and correct, discrepancies
113 - $calc_score = $ups + $downs;
114 - if ( $score > $calc_score ) {
115 - $diff = $score - $calc_score;
116 - $ups += $diff;
117 - update_post_meta( $post_id, 'bbp_voting_ups', $ups );
118 - }
119 - // Get the weighted score
120 - // $weighted_score = get_post_meta($post_id, 'bbp_voting_weighted_score', true);
121 - // Get user's vote by ID or IP
122 - $voting_log = get_post_meta( $post_id, 'bbp_voting_log', true );
123 - $voting_log = is_array( $voting_log ) ? $voting_log : []; // Set up new array
124 - $client_ip = $_SERVER['REMOTE_ADDR'];
125 - $identifier = is_user_logged_in() ? get_current_user_id() : $client_ip;
126 - $existing_vote = array_key_exists( $identifier, $voting_log ) ? $voting_log[ $identifier ] : 0;
127 - // Admin bypass?
128 - $is_admin_can_vote_unlimited = $opt['is_admin_can_vote_unlimited'] ?? false;
129 - $admin_bypass = current_user_can( 'administrator' ) && $is_admin_can_vote_unlimited;
130 - // View only score?
131 - // View only for visitors option
132 - $is_disabled_voting_non_logged = $opt['is_disabled_voting_for_non_logged_users'] ?? false;
133 - $view_only = ! is_user_logged_in() && $is_disabled_voting_non_logged;
134 - if ( ! $view_only ) {
135 - // View only for closed topic option.
136 - if ( $current_action !== 'bbp_voting_cpt' ) {
137 - $topic_id = $this_post_type == $topic_post_type ? $post_id : bbp_get_reply_topic_id( $post_id );
138 - $topic_status = get_post_status( $topic_id );
139 - $is_diabled_closed_topics = $opt['is_disabled_voting_closed_topics'] ?? false;
140 - $view_only = ( 'closed' == $topic_status && $is_diabled_closed_topics ) ? true : false;
141 - }
142 - if ( ! $view_only ) {
143 - // View only for author of post.
144 - $is_disabled_own_topic_reply = $opt['is_disabled_voting_own_topic_reply'] ?? false;
145 - $view_only = ( $is_disabled_own_topic_reply && $post->post_author == get_current_user_ID() ) ? true : false;
146 - }
147 - }
148 - // Show labels?
149 - // Disable down votes?
150 - $disable_down = $opt['is_down_votes_disabled'] ?? 0;
151 - $vote_number_display = $opt['vote_numbers_display'] ?? 'hover';
152 -
153 - // How to display vote numbers?
154 - $display_vote_nums = 'num-' . $vote_number_display;
155 - // Start HTML
156 - $html = '';
157 - $float = in_array( current_action(), [ 'bbp_theme_before_reply_content', 'bbp_theme_before_topic_content', 'bbp_voting_cpt' ] );
158 - $html .= '<div class="bbp-voting bbp-voting-post-' . $post_id . ( $view_only ? ' view-only' : ( $existing_vote == 1 ? ' voted-up' : ( $existing_vote == -1 ? ' voted-down' : '' ) ) ) . ( $admin_bypass ? ' admin-bypass' : '' ) . ( $float ? ' bbp-voting-float' : '' ) . '">';
159 -
160 - // Adds the word 'helpful' in red above the arrow.
161 - $is_label = $opt['is_label'] ?? true;
162 -
163 - if ( $is_label ) {
164 - $upvote_label = ! empty( $opt['upvote_label'] ) ? $opt['upvote_label'] : '';
165 - $html .= '<div class="bbp-voting-label helpful">' . $upvote_label . '</div>';
166 - }
167 - if ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) {
168 - // AMP = No JS
169 - $post_url = admin_url( 'admin-ajax.php' );
170 - // Up vote
171 - $plusups = $ups ? '+' . $ups : ' ';
172 - $html .= '<form name="amp-form' . $post_id . '" method="post" action-xhr="' . $post_url . '" target="_top" on="submit-success: AMP.setState({\'voteup' . $post_id . '\': ' . ( $ups + 1 ) . '})">
173 - <input type="hidden" name="action" value="bbpress_post_vote_link_clicked">
174 - <input type="hidden" name="post_id" value="' . $post_id . '" />
175 - <input type="hidden" name="direction" value="1" />
176 - <input type="submit" class="nobutton upvote-amp" value="🔺" />
177 - <span class="vote up" [text]="voteup' . $post_id . ' ? \'+\' + voteup' . $post_id . ' : \'' . $plusups . '\'">' . $plusups . '</span>
178 - </form>';
179 - // Display current vote count for post
180 - // $html .= '<div class="score">'. $score. '</div>';
181 - // $html .= '<div class="score" style="background-color: rgb('. floor((1 - $score) * 255). ', '.floor($score * 255).', 0); width:'.floor($score * 100).'%;"></div>';
182 - // Down vote
183 - if ( $disable_down === '1' ) {
184 - $html .= '<form name="amp-form' . $post_id . '" method="post" action-xhr="' . $post_url . '" target="_top" on="submit-success: AMP.setState({\'votedown' . $post_id . '\': ' . ( $downs - 1 ) . '})">
185 - <input type="hidden" name="action" value="bbpress_post_vote_link_clicked">
186 - <input type="hidden" name="post_id" value="' . $post_id . '" />
187 - <input type="hidden" name="direction" value="-1" />
188 - <input type="submit" class="nobutton downvote-amp" value="🔻" />
189 - <span class="vote down" [text]="votedown' . $post_id . ' || \'' . ( $downs ? $downs : ' ' ) . '\'">' . ( $downs ? $downs : '' ) . '</span>
190 - </form>';
191 - }
192 - } else {
193 - // Normal JS AJAX version
194 - // Up vote
195 - $html .= '<a class="vote up ' . $display_vote_nums . '" data-votes="' . ( $ups ? '+' . $ups : '' ) . '" onclick="bbpress_post_vote_link_clicked(' . $post_id . ', 1); return false;">Up</a>';
196 - // Display current vote count for post
197 - $html .= '<div class="score">' . $score . '</div>';
198 - // Down vote
199 - if ( $disable_down === '1' ) {
200 - $html .= '<a class="vote down ' . $display_vote_nums . '" data-votes="' . ( $downs ? $downs : '' ) . '" onclick="bbpress_post_vote_link_clicked(' . $post_id . ', -1); return false;">Down</a>';
201 - }
202 - }
203 - // adds the words 'not helpful' in red below the arrow
204 - if ( $disable_down === '1' && $is_label ) {
205 - $downvote_label = ! empty( $opt['downvote_label'] ) ? $opt['downvote_label'] : '';
206 - $html .= '<div class="bbp-voting-label not-helpful">' . $downvote_label . '</div>';
207 - }
208 - if ( $this_post_type == $reply_post_type ) {
209 - $html = apply_filters( 'bbp_voting_after_reply_voting_buttons', $html, $post_id ); // TODO: take care
210 - }
211 - $html .= '</div>';
212 - // Special hidden mark after the voting buttons for using regex to strip them off of things that pull excerpts using jQuery text()
213 - $html .= '<span style="display:none;">::</span>';
214 - // return $html . $author_link;
215 - echo $html;
216 - }
217 -}
218 -
219 -// Sort by Votes
220 -
221 -add_filter( 'bbp_has_topics_query', 'sort_bbpress_posts_by_votes', 99 );
222 -add_filter( 'bbp_has_replies_query', 'sort_bbpress_posts_by_votes', 99 );
223 -
224 -function sort_bbpress_posts_by_votes( $args = [] ) {
225 - $opt = get_option( 'bbp_core_settings' );
226 -
227 - $forum_id = bbp_get_forum_id();
228 - // if($forum_id === 0) return $args;
229 - // $this_post_type = isset($args['post_type']) ? $args['post_type'] : bbp_voting_get_current_post_type();
230 - // $this_post_type = bbp_voting_get_current_post_type();
231 - $forum_post_type = bbp_get_forum_post_type();
232 - $topic_post_type = bbp_get_topic_post_type();
233 - switch ( current_filter() ) {
234 - case 'bbp_has_topics_query':
235 - $this_post_type = $forum_post_type;
236 - $post_setting = get_post_meta( $forum_id, 'sort_bbpress_topics_by_votes_on_forum', true );
237 - $broad_enable = $opt['is_sort_topic_by_votes'] ?? 0;
238 - break;
239 - case 'bbp_has_replies_query':
240 - $this_post_type = $topic_post_type;
241 - $post_setting = get_post_meta( $forum_id, 'sort_bbpress_replies_by_votes_on_forum', true );
242 - $broad_enable = $opt['is_sort_reply_by_votes'] ?? 0;
243 - break;
244 - default:
245 - return $args;
246 - }
247 - // Do "allowed" checks
248 - if ( isset( $_GET['bbp-voting-sort'] ) ) {
249 - // Sort dropdown used, skip the rest of the checks
250 - if ( $_GET['bbp-voting-sort'] === 'best' ) {
251 - // Proceed with score sorting
252 - } elseif ( $_GET['bbp-voting-sort'] === 'default' || $_GET['bbp-voting-sort'] === '' ) {
253 - // bbp default non-score sort
254 - return $args;
255 - }
256 - } else {
257 - // Sort dropdown not used... check the settings
258 - if ( ! empty( $post_setting ) ) {
259 - // Forum-specific override is set (not Default)
260 - if ( $post_setting === 'false' ) {
261 - return $args;
262 - }
263 - } else {
264 - // Use broad disable settings
265 - if ( $broad_enable === '1' ) {
266 - return $args;
267 - }
268 - }
269 - if ( ! apply_filters( 'bbp_voting_allowed_on_forum', true, $forum_id ) ) {
270 - return $args;
271 - }
272 - }
273 - // Done with "allowed" checks... let's do this
274 - // Filter the sort meta key. Default = bbp_voting_score
275 - $sort_meta_key = apply_filters( 'bbp_voting_sort_meta_key', 'bbp_voting_score' );
276 -
277 - // Reset for testing only -------------------------------------
278 - // $argsreset = $args;
279 - // $query = new WP_Query($argsreset);
280 - // foreach($query->posts as $reply) {
281 - // delete_post_meta($reply->ID, $sort_meta_key);
282 - // }
283 - // -----------------------------------
284 -
285 - // Find any replies that are missing the bbp_voting_score post meta and fill them with 0
286 - $args2 = $args;
287 - $args2['meta_query'] = [
288 - [
289 - 'key' => $sort_meta_key,
290 - 'compare' => 'NOT EXISTS',
291 - 'value' => '',
292 - ],
293 - ];
294 - $query = new WP_Query( $args2 );
295 - foreach ( $query->posts as $reply ) {
296 - $fill_in_default = apply_filters( 'bbp_voting_sort_meta_key_default_value', '0', $reply->ID );
297 - update_post_meta( $reply->ID, $sort_meta_key, $fill_in_default );
298 - }
299 -
300 - // Now that all missing scores are filled in, we can sort the original args by the score
301 - // $args['meta_key'] = 'bbp_voting_score';
302 - // $args['orderby'] = [
303 - // 'post_type' => 'DESC',
304 - // 'meta_value_num' => 'DESC',
305 - // 'date' => 'DESC'
306 - // ];
307 - unset( $args['meta_key'] );
308 - unset( $args['meta_type'] );
309 - unset( $args['orderby'] );
310 - unset( $args['order'] );
311 - $args['meta_query'] = [
312 - 'relation' => 'AND',
313 - 'score_clause' => [
314 - 'key' => $sort_meta_key,
315 - 'compare' => 'EXISTS',
316 - // 'type' => 'DECIMAL' // https://stackoverflow.com/questions/30018711/wordpress-meta-query-not-working-with-decimal-type
317 - ],
318 - ];
319 - if ( $sort_meta_key == 'bbp_voting_score' ) {
320 - $args['meta_query']['score_clause']['type'] = 'NUMERIC';
321 - }
322 - $args['orderby'] = [
323 - 'post_type' => 'DESC',
324 - 'score_clause' => 'DESC',
325 - ];
326 - if ( $this_post_type === $topic_post_type ) {
327 - // Add Date as another orderby for topics on a forum
328 - $args['orderby']['date'] = 'ASC';
329 - }
330 - if ( $this_post_type === $forum_post_type ) {
331 - // Add Freshness as another orderby for topics on a forum
332 - $args['meta_query']['orderby_freshness'] = [
333 - 'key' => '_bbp_last_active_time',
334 - 'type' => 'DATETIME',
335 - ];
336 - $args['orderby']['orderby_freshness'] = 'DESC';
337 - }
338 - return $args;
339 -}
340 -
341 -add_action( 'init', 'bbp_voting_lead_topic' );
342 -function bbp_voting_lead_topic() {
343 - $opt = get_option( 'bbp_core_settings' );
344 -
345 - if ( $opt['is_lead_topic_broken'] ?? false ) {
346 - add_filter( 'bbp_show_lead_topic', '__return_true' );
347 - }
348 -}
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 +}