• Greetings,

    I encountered an issue while using the plugin.

    One of my users lost a set of predictions due to a multi-device session scenario:

    1. The user opened the prediction page on Device A and submitted a set of predictions.
    2. Later, the user logged in on Device B and submitted additional predictions.
    3. Afterwards, the user returned to Device A and refreshed the page.
    4. The browser displayed the message “Confirm Form Resubmission” (or “Send Form Information Again”), and the user clicked OK.
    5. As a result, the predictions associated with that page were deleted, even though the current time was already past the match kickoff time.

    After reviewing the code, my understanding is that the form resubmission bypasses the expected validation flow and enters the processing function directly.

    It appears that the code first executes the logic that deletes the existing prediction records before validating whether the kickoff time has already passed. As a consequence, the records are removed, and only afterwards the kickoff time validation is evaluated.

    From my perspective, the kickoff time validation should occur before any delete or update operation is performed, especially in cases where the browser resubmits a previously submitted form.

    /* wcp-predict.class.php */
    ....
    /**
    * Save this submitted prediction
    */
    function save_prediction($match) {
    global $wpdb;
    global $current_user;

    $ret = false;
    if (!is_array($match)) {
    return false;
    }

    $this->setMessage(__('Prediction saved', WCP_TD));

    /**
    * Clean input
    */
    foreach ($match as $key=>$m) {
    $match[$key] = array_map('trim', $m);
    }

    /**
    * Validate, both home and away scores must be present or both blank.
    */
    foreach ($match as $key=>$m) {
    if ((!empty($m['home']) || !empty($m['away'])) && (!$this->isint($m['home']) || !$this->isint($m['away']))) {
    $this->setMessage(__('Match score must be numeric', WCP_TD), true);
    return false;
    }

    /**
    * Both penality scores must be present or blank.
    */
    if (isset($m['pen_home'])) {
    if ((!empty($m['pen_home']) || !empty($m['pen_away'])) && !$this->isint($m['pen_home'])) {
    $this->setMessage(__('Match score must be numeric', WCP_TD), true);
    return false;
    }
    if (empty($m['pen_home'])) $match[$key]['pen_home'] = 0;
    } else {
    $match[$key]['pen_home'] = 0;
    }
    if (isset($m['pen_away'])) {
    if ((!empty($m['pen_home']) || !empty($m['pen_away'])) && !$this->isint($m['pen_away'])) {
    $this->setMessage(__('Match score must be numeric', WCP_TD), true);
    return false;
    }
    if (empty($m['pen_away'])) $match[$key]['pen_away'] = 0;
    } else {
    $match[$key]['pen_away'] = 0;
    }

    /**
    * Entered penalities but no goals !
    */
    if (($match[$key]['pen_home'] > 0 || $match[$key]['pen_away'] > 0) && (!$this->isint($m['home']) || !$this->isint($m['away']))) {
    $this->setMessage(__('Match score must be numeric', WCP_TD), true);
    return false;
    }

    /**
    * Entered penalities but not a draw
    */
    if (($match[$key]['pen_home'] > 0 || $match[$key]['pen_away'] > 0) && ($m['home'] != $m['away'])) {
    $this->setMessage(__('You can only enter penalties for a draw', WCP_TD), true);
    return false;
    }
    }

    /**
    * Save each prediction - checking that they are before the prediction deadline
    * as the user have have left the form displayed over the deadline !
    */

    wp_get_current_user();
    foreach ($match as $key=>$m) {

    /**
    * Don't save 'blank' predictions
    */
    if ($m['home'] == '' && $m['away'] == '') {

    $sql = "DELETE FROM
    {$wpdb->prefix}{$this->prefix}prediction
    WHERE
    user_id = %d AND match_id = %d";
    $wpdb->query($wpdb->prepare($sql, $current_user->ID, $key));
    continue;
    }

    $sql = "SELECT kickoff FROM {$wpdb->prefix}{$this->prefix}match WHERE match_id = %d";
    $match_start = $wpdb->get_row($wpdb->prepare($sql, $key));
    if (!$match_start) {
    $this->setMessage(__('Missing match. Prediction not saved.', WCP_TD), true);
    return false;
    }

    $sql = "SELECT prediction_id
    FROM
    {$wpdb->prefix}{$this->prefix}prediction p
    WHERE
    user_id = %d AND match_id = %d";
    $row = $wpdb->get_row($wpdb->prepare($sql, $current_user->ID, $key));
    if ($row) {
    $sql = "UPDATE {$wpdb->prefix}{$this->prefix}prediction
    SET
    home_goals = %d,
    away_goals = %d,
    home_penalties = %d,
    away_penalties = %d,
    wwhen = UTC_TIMESTAMP()
    WHERE
    prediction_id = %d AND UTC_TIMESTAMP() < %s";
    $ret = $wpdb->query($wpdb->prepare($sql, $m['home'], $m['away'], $m['pen_home'], $m['pen_away'], $row->prediction_id,$match_start->kickoff));
    } else {

    $sql = "INSERT INTO {$wpdb->prefix}{$this->prefix}prediction
    (user_id, match_id, home_goals, away_goals, home_penalties, away_penalties, points, wwhen)
    SELECT %d, %d, %d, %d, %d, %d, 0, UTC_TIMESTAMP() FROM DUAL
    WHERE
    UTC_TIMESTAMP() < %s";
    $ret = $wpdb->query($wpdb->prepare($sql, $current_user->ID, $key, $m['home'], $m['away'], $m['pen_home'], $m['pen_away'], $match_start->kickoff));
    }


    if ($ret === false) { // can return 0 as successfully modified zero rows
    $this->setMessage(__('Error saving prediction', WCP_TD) . $wpdb->last_error, true);
    return false;
    }
    if ($ret == 0) {
    // No rows updated/inserted, so prediction must be too late !!!
    $this->setMessage(__('Some, or all, of your predictions are too late and will not be counted.', WCP_TD), true);
    }
    }
    return $ret;
    }

You must be logged in to reply to this topic.