HEX
Server: LiteSpeed
System: Linux srv158.niagahoster.com 4.18.0-553.30.1.lve.el8.x86_64 #1 SMP Tue Dec 3 01:21:19 UTC 2024 x86_64
User: u1694298 (3732)
PHP: 7.4.33
Disabled: symlink,shell_exec,exec,popen,system,dl,passthru,escapeshellarg,escapeshellcmd,show_source,pcntl_exec
Upload Files
File: /home/u1694298/www/wp-content/plugins/yop-poll/includes/Models/class-model-vote.php
<?php
namespace YopPoll\Models;

use YopPoll\REST\REST_Polls;
use YopPoll\Database\Migrator;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Model_Vote extends Model_Base {

	protected $table = 'votes';

	public function get_by_poll( $poll_id ) {
		return $this->find_by( 'poll_id', $poll_id );
	}

	public function delete_by_poll( $poll_id ) {
		return $this->delete_by( 'poll_id', $poll_id );
	}

	public function has_voted( $poll_id, $args = array() ) {
		global $wpdb;
		$table = $this->get_table();

		$where_clauses = array( 'poll_id = %d', "status = 'active'" );
		$where_values  = array( $poll_id );

		if ( ! empty( $args['user_id'] ) ) {
			$where_clauses[] = 'user_id = %d';
			$where_values[]  = $args['user_id'];
		}

		if ( ! empty( $args['ipaddress'] ) ) {
			$where_clauses[] = 'ipaddress = %s';
			$where_values[]  = $args['ipaddress'];
		}

		if ( ! empty( $args['tracking_id'] ) ) {
			$where_clauses[] = 'tracking_id = %s';
			$where_values[]  = $args['tracking_id'];
		}

		if ( ! empty( $args['voter_id'] ) ) {
			$where_clauses[] = 'voter_id = %s';
			$where_values[]  = $args['voter_id'];
		}

		if ( ! empty( $args['voter_fingerprint'] ) ) {
			$where_clauses[] = 'voter_fingerprint = %s';
			$where_values[]  = $args['voter_fingerprint'];
		}

		if ( ! empty( $args['user_email'] ) ) {
			$where_clauses[] = 'user_email = %s';
			$where_values[]  = $args['user_email'];
		}

		$where_sql = implode( ' AND ', $where_clauses );

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $table/$where_sql are built from $wpdb->prefix and hardcoded clauses; live vote-check query must not be cached.
		return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE {$where_sql}", $where_values ) ) > 0;
	}

	/**
	 * Returns the most recent active vote row for the given poll matching the provided criteria,
	 * or null if no such row exists.
	 *
	 * @param int   $poll_id
	 * @param array $args  Keys: user_id, ipaddress, tracking_id, voter_id, voter_fingerprint, user_email
	 * @return array|null
	 */
	public function get_last_vote( int $poll_id, array $args ): ?array {
		global $wpdb;
		$table = $this->get_table();

		$where_clauses = array( 'poll_id = %d', "status = 'active'" );
		$where_values  = array( $poll_id );

		if ( ! empty( $args['user_id'] ) ) {
			$where_clauses[] = 'user_id = %d';
			$where_values[]  = $args['user_id'];
		}

		if ( ! empty( $args['ipaddress'] ) ) {
			$where_clauses[] = 'ipaddress = %s';
			$where_values[]  = $args['ipaddress'];
		}

		if ( ! empty( $args['tracking_id'] ) ) {
			$where_clauses[] = 'tracking_id = %s';
			$where_values[]  = $args['tracking_id'];
		}

		if ( ! empty( $args['voter_id'] ) ) {
			$where_clauses[] = 'voter_id = %s';
			$where_values[]  = $args['voter_id'];
		}

		if ( ! empty( $args['voter_fingerprint'] ) ) {
			$where_clauses[] = 'voter_fingerprint = %s';
			$where_values[]  = $args['voter_fingerprint'];
		}

		if ( ! empty( $args['user_email'] ) ) {
			$where_clauses[] = 'user_email = %s';
			$where_values[]  = $args['user_email'];
		}

		$where_sql = implode( ' AND ', $where_clauses );

		$row = $wpdb->get_row( // phpcs:ignore WordPress.DB.DirectDatabaseQuery, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $table built from $wpdb->prefix . YOP_POLL_TABLE_PREFIX.
			// phpcs:ignore WordPress.DB.PreparedSQL, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- $where_sql is built from hardcoded clauses with %d/%s placeholders.
			$wpdb->prepare( "SELECT * FROM {$table} WHERE {$where_sql} ORDER BY added_date DESC LIMIT 1", $where_values ),
			ARRAY_A
		);

		return $row ?: null;
	}

	/**
	 * Returns the most recent active vote row for a specific WordPress user on a poll.
	 *
	 * @param int $poll_id
	 * @param int $user_id
	 * @return array|null
	 */
	public function find_latest_by_user( int $poll_id, int $user_id ): ?array {
		global $wpdb;
		$table = $this->get_table();

		$row = $wpdb->get_row( // phpcs:ignore WordPress.DB.DirectDatabaseQuery, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $table built from $wpdb->prefix . YOP_POLL_TABLE_PREFIX.
			$wpdb->prepare( "SELECT * FROM {$table} WHERE poll_id = %d AND user_id = %d AND status = 'active' ORDER BY added_date DESC LIMIT 1", $poll_id, $user_id ), // phpcs:ignore WordPress.DB.PreparedSQL
			ARRAY_A
		);

		return $row ?: null;
	}

	/**
	 * Delete a vote and reverse all associated counters and orphaned rows.
	 *
	 * @param int $vote_id
	 */
	public function delete_with_cleanup( int $vote_id ): void {
		$vote = $this->find( $vote_id );
		if ( ! $vote ) {
			return;
		}

		$poll_id   = (int) $vote['poll_id'];
		$vote_data = Migrator::decode_meta( $vote['vote_data'] ?? '' );

		$sub_model    = new Model_Subelement();
		$answer_count = 0;

		foreach ( $vote_data['elements'] ?? [] as $element ) {
			$element_id = (int) $element['id'];
			foreach ( $element['data'] ?? [] as $item ) {
				$answer_id    = (int) $item['id'];
				$answer_value = (string) ( $item['data'][0] ?? '' );
				$answer_count++;

				if ( $answer_id > 0 ) {
					// Normal answer: decrement subelement counter.
					$sub_model->decrement_submits( $answer_id );
					// If this is a dynamically-created "other" answer, remove it when no votes remain.
					$sub = $sub_model->find( $answer_id );
					if ( $sub && 'other' === $sub['stype'] && (int) $sub['total_submits'] <= 0 ) {
						$sub_model->delete( $answer_id );
					}
				} elseif ( '' !== $answer_value ) {
					// "Other" answer (PATH A): find matching 'other' subelement and decrement.
					$other_sub_id = $sub_model->find_other_by_text( $element_id, $answer_value );
					if ( $other_sub_id > 0 ) {
						$sub_model->decrement_submits( $other_sub_id );
						// If no votes remain for this dynamically-created "Other" answer, remove it.
						$other_sub = $sub_model->find( $other_sub_id );
						if ( $other_sub && (int) $other_sub['total_submits'] <= 0 ) {
							$sub_model->delete( $other_sub_id );
						}
					}
				}
			}
		}

		// "Other" answer (PATH B): remove any other_answers rows for this vote.
		$other_model = new Model_Other_Answer();
		$other_model->delete_by_vote( $vote_id );

		// Decrement poll counters.
		$poll_model = new Model_Poll();
		$poll_model->decrement_submits( $poll_id );
		if ( $answer_count > 0 ) {
			$poll_model->increment_submited_answers( $poll_id, -$answer_count );
		}

		// Refresh poll cache so frontend/results reflect the deletion immediately.
		REST_Polls::refresh_poll_cache( $poll_id );

		// Delete the vote row itself.
		$this->delete( $vote_id );
	}

	/**
	 * Count active votes for a user on a poll, identified by user_id or user_email.
	 *
	 * @param int    $poll_id
	 * @param array  $args  Keys: user_id (int), user_email (string)
	 * @return int
	 */
	public function count_active_for_user( int $poll_id, array $args ): int {
		global $wpdb;
		$table = $this->get_table();

		$where_clauses = array( 'poll_id = %d', "status = 'active'" );
		$where_values  = array( $poll_id );

		if ( ! empty( $args['user_id'] ) ) {
			$where_clauses[] = 'user_id = %d';
			$where_values[]  = $args['user_id'];
		}

		if ( ! empty( $args['user_email'] ) ) {
			$where_clauses[] = 'user_email = %s';
			$where_values[]  = $args['user_email'];
		}

		$where_sql = implode( ' AND ', $where_clauses );

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $table/$where_sql are built from $wpdb->prefix and hardcoded clauses; live vote-count query must not be cached.
		return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE {$where_sql}", $where_values ) );
	}
}