# bbp-core/2.5.1/includes/features/bbp_voting/helpers.php

Forumax – AI Powered Advanced Community Forum Plugin, version 2.5.1. 273 lines.

- Page: https://pluginprobe.com/plugins/bbp-core/2.5.1/code/includes/features/bbp_voting/helpers.php
- Raw: https://pluginprobe.com/plugins/bbp-core/2.5.1/raw/includes/features/bbp_voting/helpers.php
- Modified: 2026-10-12T12:43:18+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/bbp-core/2.5.1/code/includes/features/bbp_voting/helpers.php#L10-L20`.

```php
<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

if ( ! function_exists( 'bbp_voting_get_plugin_install_link' ) ) {
    function bbp_voting_get_plugin_install_link( $plugin, $action = 'install-plugin' ) {
        return esc_url(
            wp_nonce_url(
                add_query_arg(
                    [
                        'action' => $action,
                        'plugin' => $plugin,
                    ],
                    admin_url( 'update.php' )
                ),
                $action . '_' . $plugin
            )
        );
    }
}


if ( ! function_exists( 'bbp_voting_get_plugin_activate_link' ) ) {
	function bbp_voting_get_plugin_activate_link( $plugin, $action = 'activate' ) {
		if ( strpos( $plugin, '/' ) ) {
			$plugin = str_replace( '\/', '%2F', $plugin );
		}
		$url                = sprintf( admin_url( 'plugins.php?action=' . $action . '&plugin=%s&plugin_status=all&paged=1&s' ), $plugin );
		$_REQUEST['plugin'] = $plugin;
		$url                = wp_nonce_url( $url, $action . '-plugin_' . $plugin );
		return $url;
	}
}


if ( ! function_exists( 'bbp_voting_field' ) ) {
	function bbp_voting_field( $key, $name, $label = '', $description = '', $type = 'bool', $pro = false ) {
		$name_and_id = 'name="' . esc_attr( $key ) . '" id="' . esc_attr( $key ) . '"';
		$teaser      = defined( 'BBPVOTINGPRO' ) ? false : $pro;
		$attributes  = $teaser ? 'disabled' : $name_and_id;
		?>
		<tr valign="top">
			<th scope="row">
				<?php echo esc_html( $name ); ?>:
				<?php if ( $pro ) { ?>
					<a href="<?php echo esc_url( 'https://wpforthewin.com/product/bbpress-voting-pro/' ); ?>" target="_blank" rel="noopener noreferrer">
						<span class="bbp-voting-pro-badge bbp-voting-pro-green">Pro</span>
					</a>
				<?php } ?>
			</th>
			<td>
				<?php if ( $type === 'bool' ) { ?>
					<input type="checkbox" <?php echo wp_kses_post( $attributes ); ?> value="true"
						<?php checked( get_option( $key ), 'true' ); ?> />
				<?php } elseif ( is_array( $type ) ) { ?>
					<select <?php echo wp_kses_post( $attributes ); ?>>
						<?php foreach ( $type as $option ) { ?>
							<option value="<?php echo esc_attr( $option ); ?>" <?php selected( get_option( $key ), $option ); ?>>
								<?php echo esc_html( ucwords( str_replace( '-', ' ', $option ) ) ); ?>
							</option>
						<?php } ?>
					</select>
				<?php } else { ?>
					<input type="<?php echo esc_attr( $type ); ?>" <?php echo wp_kses_post( $attributes ); ?> 
						value="<?php echo esc_attr( get_option( $key ) ); ?>" data-lpignore="true" />
				<?php } ?>

				<?php if ( ! empty( $label ) ) { ?>
					<label for="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $label ); ?></label>
				<?php } ?>

				<?php if ( ! empty( $description ) ) { ?>
					<p class="description"><?php echo wp_kses_post( $description ); ?></p>
				<?php } ?>
			</td>
		</tr>
		<?php
	}
}

if ( ! function_exists( 'bbp_voting_get_current_post_type' ) ) {
	function bbp_voting_get_current_post_type() {
		$this_post_id = bbp_get_reply_id() ?: bbp_get_topic_id();
		return get_post_type( $this_post_id );
	}
}

if ( ! function_exists( 'bbp_voting_get_post_type_by_id' ) ) {
	function bbp_voting_get_post_type_by_id( $post_id ) {
		$this_post_id = bbp_get_reply_id( $post_id ) ?: bbp_get_topic_id( $post_id );
		return get_post_type( $this_post_id );
	}
}

if ( ! class_exists( 'WilsonConfidenceIntervalCalculator' ) ) {
	class WilsonConfidenceIntervalCalculator {
		/**
		 * Computed value for confidence (z)
		 *
		 * These values were computed using Ruby's Statistics2.pnormaldist function
		 * 1.645 = 90% confidence
		 * 1.959964 = 95.0% confidence
		 * 2.241403 = 97.5% confidence
		 */
		public function getScore( int $positiveVotes, int $totalVotes, float $confidence = 1.645 ) : float {
			return (float) $totalVotes ? $this->lowerBound( $positiveVotes, $totalVotes, $confidence ) : 0;
		}
		private function lowerBound( int $positiveVotes, int $totalVotes, float $confidence ) : float {
			$phat        = 1.0 * $positiveVotes / $totalVotes;
			$numerator   = $this->calculationNumerator( $totalVotes, $confidence, $phat );
			$denominator = $this->calculationDenominator( $totalVotes, $confidence );
			return $numerator / $denominator;
		}
		private function calculationDenominator( int $total, float $z ) : float {
			return 1 + $z * $z / $total;
		}
		private function calculationNumerator( int $total, float $z, float $phat ) : float {
			return $phat + $z * $z / ( 2 * $total ) - $z * sqrt( ( $phat * ( 1 - $phat ) + $z * $z / ( 4 * $total ) ) / $total );
		}
	}
}


if ( ! function_exists( 'bbp_voting_parse_args' ) ) {
	function bbp_voting_parse_args( $args, $defaults = [], $filter_key = '' ) {

		// Setup a temporary array from $args
		if ( is_object( $args ) ) {
			$r = get_object_vars( $args );
		} elseif ( is_array( $args ) ) {
			$r =& $args;
		} else {
			$r = [];
			wp_parse_str( $args, $r );
		}

		// Passively filter the args before the parse
		if ( ! empty( $filter_key ) ) {
			$r = apply_filters( "bbp_voting_before_{$filter_key}_parse_args", $r, $args, $defaults );
		}

		// Parse
		if ( is_array( $defaults ) && ! empty( $defaults ) ) {
			$r = array_merge( $defaults, $r );
		}

		// Aggressively filter the args after the parse
		if ( ! empty( $filter_key ) ) {
			$r = apply_filters( "bbp_voting_after_{$filter_key}_parse_args", $r, $args, $defaults );
		}

		// Return the parsed results
		return $r;
	}
}


if ( ! function_exists( 'forumax_init_voting_scores' ) ) {
	/**
	 * Initialize voting scores for new topics and replies.
	 *
	 * @param int $post_id Post ID.
	 */
	function forumax_init_voting_scores( $post_id ) {
		update_post_meta( $post_id, 'bbp_voting_score', '0' );
		update_post_meta( $post_id, 'bbp_voting_ups', '0' );
		update_post_meta( $post_id, 'bbp_voting_downs', '0' );
		update_post_meta( $post_id, 'bbp_voting_log', [] );
	}
	add_action( 'bbp_new_topic', 'forumax_init_voting_scores' );
	add_action( 'bbp_new_reply', 'forumax_init_voting_scores' );
}

if ( ! function_exists( 'forumax_ensure_voting_score_meta' ) ) {
	/**
	 * Give every saved topic/reply a vote score, including ones created in
	 * wp-admin or by importers, so "sort by votes" never drops them.
	 *
	 * @param int $post_id Post ID.
	 */
	function forumax_ensure_voting_score_meta( $post_id ) {
		if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
			return;
		}

		add_post_meta( $post_id, 'bbp_voting_score', '0', true );
	}
	add_action( 'save_post_topic', 'forumax_ensure_voting_score_meta' );
	add_action( 'save_post_reply', 'forumax_ensure_voting_score_meta' );
}

if ( ! function_exists( 'forumax_backfill_voting_score_meta_once' ) ) {
	/**
	 * One-time bulk backfill of bbp_voting_score for existing topics/replies.
	 *
	 * Replaces the old per-page-view backfill, which ran an extra query and
	 * stored a new transient for every distinct topic list.
	 */
	function forumax_backfill_voting_score_meta_once() {
		if ( get_option( 'forumax_vote_score_backfilled' ) ) {
			return;
		}

		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->query(
			"INSERT INTO {$wpdb->postmeta} ( post_id, meta_key, meta_value )
			SELECT p.ID, 'bbp_voting_score', '0'
			FROM {$wpdb->posts} p
			LEFT JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID AND pm.meta_key = 'bbp_voting_score'
			WHERE p.post_type IN ( 'topic', 'reply' ) AND pm.meta_id IS NULL"
		);

		update_option( 'forumax_vote_score_backfilled', 1 );

		// Drop the transients the old per-page backfill left behind.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s",
				$wpdb->esc_like( '_transient_frmx_vote_backfill_' ) . '%',
				$wpdb->esc_like( '_transient_timeout_frmx_vote_backfill_' ) . '%'
			)
		);
	}
	add_action( 'admin_init', 'forumax_backfill_voting_score_meta_once' );
}

if ( ! function_exists( 'forumax_backfill_voting_sort_meta' ) ) {
	/**
	 * Backfill a custom (filtered) sort meta key for the posts of one query.
	 *
	 * Only used when bbp_voting_sort_meta_key points at a non-default key.
	 * Results are remembered in the object cache for the request/day.
	 *
	 * @param string $sort_meta_key Meta key used for sorting.
	 * @param array  $args          bbPress query args.
	 */
	function forumax_backfill_voting_sort_meta( $sort_meta_key, $args ) {
		$cache_key = 'vote_backfill_' . md5( $sort_meta_key . wp_json_encode( $args ) );
		if ( false !== wp_cache_get( $cache_key, 'forumax' ) ) {
			return;
		}

		$query = new WP_Query(
			array_merge(
				$args,
				[
					'meta_query'             => [
						[
							'key'     => $sort_meta_key,
							'compare' => 'NOT EXISTS',
						],
					],
					'fields'                 => 'ids',
					'no_found_rows'          => true,
					'update_post_meta_cache' => false,
					'update_post_term_cache' => false,
				]
			)
		);

		foreach ( $query->posts as $post_id ) {
			$default_value = apply_filters( 'bbp_voting_sort_meta_key_default_value', '0', $post_id );
			update_post_meta( $post_id, $sort_meta_key, $default_value );
		}

		wp_cache_set( $cache_key, 1, 'forumax', DAY_IN_SECONDS );
	}
}

```
