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
← All changes | includes/features/bbp_voting/frontend.php +357 -348 1.2.4 → 2.5.1 View file →
@@ -1,348 +1,357 @@
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 + // 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 +}