Plugin Directory

Changeset 3754699


Ignore:
Timestamp:
10/19/2026 12:19:06 PM (2 weeks ago)
Author:
quantumcloud
Message:

Fixed an issue with form

Location:
conversational-forms
Files:
1646 added
2 edited

Legend:

Unmodified
Added
Removed
  • conversational-forms/trunk/qcformbuilder-core.php

    r3745718 r3754699  
    66 * Author: ChatBot for WordPress - WPBot
    77 * @author    QuantumCloud
    8  * Version: 1.5.2
     8 * Version: 1.5.3
    99 * Donate link: https://www.wpbot.pro/
    1010 * @author    QuantumCloud
    … …  
    9999        }
    100100        if ( ! defined('WFBCORE_VER') ) {
    101                 define( 'WFBCORE_VER', '1.5.1' );
     101                define( 'WFBCORE_VER', '1.5.3' );
    102102        }
    103103        if ( ! defined('WFBCORE_EXTEND_URL') ) {
    … …  
    353353                        $firstfield = qc_get_first_field($form['layout_grid']['fields']);
    354354                        $field = $fields[$firstfield];
     355                        $field = qc_form_replace_field_tags( $field, $fields, 0, qc_form_visitor_tags() );
    355356                        echo json_encode($field);
    356357                }
    … …  
    391392
    392393                // phpcs:disable WordPress.Security.NonceVerification.Missing
    393                 $entry        = isset( $_POST['entry'] ) ? sanitize_text_field( wp_unslash( $_POST['entry'] ) ) : '';
     394                // absint() so '', 'undefined', 'null' etc. from the client all mean "new entry" (0).
     395                $entry        = isset( $_POST['entry'] ) ? absint( wp_unslash( $_POST['entry'] ) ) : 0;
    394396                $name         = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
    395397                $entry_nonce  = isset( $_POST['entry_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['entry_nonce'] ) ) : '';
    … …  
    400402                // client.  An attacker who knows (or guesses) another visitor's entry ID
    401403                // cannot insert values into it without also possessing a valid nonce.
    402                 if ( $entry != 0 ) {
    403                         if ( ! wp_verify_nonce( $entry_nonce, 'wpbot_entry_' . absint( $entry ) ) ) {
     404                if ( $entry > 0 ) {
     405                        if ( empty( $entry_nonce ) || ! wp_verify_nonce( $entry_nonce, 'wpbot_entry_' . $entry ) ) {
    404406                                wp_send_json_error( array( 'message' => 'Invalid or expired session token.' ), 403 );
    405407                        }
    406408                }
    407409       
    408                 if($entry==0){
     410                if ( 0 === $entry ) {
    409411
    410412                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
    … …  
    428430                if ( $answer !== '' ) {
    429431
    430                         // Encode the answer to a safe scalar string before storage so that
    431                         // no raw PHP structure (array, object, serialized data) is ever
    432                         // written directly to the database column.
     432                        // Store a plain, sanitized scalar string. Serialized payloads were
     433                        // already rejected above, so no PHP structure can reach the DB.
     434                        // Do NOT json-encode: the added quotes break condition matching in
     435                        // qcld_condition_check() and show up in the notification email.
    433436                        if ( is_array( $answer ) ) {
    434437                                // Flatten multi-select / checkbox answers to a readable CSV string.
    435                                 // This keeps the stored value compatible with the plain-string
    436                                 // comparisons and display logic that reads `value` back elsewhere.
    437438                                $stored_answer = implode( ', ', $answer );
    438439                        } else {
    439                                 // wp_json_encode produces a quoted JSON string (e.g. "foo"),
    440                                 // guaranteeing a well-formed, non-object scalar is stored.
    441                                 $stored_answer = wp_json_encode( $answer );
     440                                $stored_answer = (string) $answer;
    442441                        }
    443442
    … …  
    485484                               
    486485                                $field['status'] = 'incomplete';
     486                                // Replace %slug% placeholders (e.g. "Nice to meet you %name%!") with earlier answers.
     487                                $field = qc_form_replace_field_tags( $field, $fields, $entry, qc_form_visitor_tags() );
    487488                                echo json_encode($field);
    488489
    … …  
    504505               
    505506                die();
     507        }
     508}
     509
     510if ( ! function_exists( 'qc_form_visitor_tags' ) ) {
     511        /**
     512         * Visitor details the chatbot sends with each request, usable as %name% / %email%.
     513         *
     514         * @return array
     515         */
     516        function qc_form_visitor_tags() {
     517                // phpcs:disable WordPress.Security.NonceVerification.Missing
     518                $name  = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
     519                $email = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
     520                // phpcs:enable WordPress.Security.NonceVerification.Missing
     521                if ( in_array( strtolower( $name ), array( 'undefined', 'null' ), true ) ) {
     522                        $name = '';
     523                }
     524                return array(
     525                        'name'  => $name,
     526                        'email' => $email,
     527                );
     528        }
     529}
     530
     531if ( ! function_exists( 'qc_form_replace_field_tags' ) ) {
     532        /**
     533         * Replace %field_slug% placeholders in a field's visible texts with the
     534         * answers already saved for this entry.
     535         *
     536         * @param array $field  Field about to be sent to the chatbot.
     537         * @param array $fields All fields of the form (keyed by field ID).
     538         * @param int   $entry  Entry ID.
     539         * @return array
     540         */
     541        function qc_form_replace_field_tags( $field, $fields, $entry, $visitor = array() ) {
     542                global $wpdb;
     543
     544                $entry  = absint( $entry );
     545                $fields = is_array( $fields ) ? $fields : array();
     546
     547                $table = $wpdb->prefix . 'wfb_form_entry_values';
     548                $rows  = ! $entry ? array() : $wpdb->get_results( $wpdb->prepare( "SELECT field_id, value FROM $table WHERE entry_id = %d", $entry ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- table name from $wpdb->prefix + constant
     549
     550                $answers = array();
     551                foreach ( (array) $rows as $row ) {
     552                        $value = (string) $row->value;
     553                        // Values saved by the previous version were JSON-encoded ("foo").
     554                        if ( strlen( $value ) > 1 && '"' === $value[0] && '"' === substr( $value, -1 ) ) {
     555                                $decoded = json_decode( $value );
     556                                if ( is_string( $decoded ) ) {
     557                                        $value = $decoded;
     558                                }
     559                        }
     560                        $answers[ $row->field_id ] = $value;
     561                }
     562
     563                // slug => value. Form answers first, then the visitor's name/email
     564                // known to the chatbot (only fills a tag that has no answer yet).
     565                $values = array();
     566                foreach ( $fields as $field_id => $form_field ) {
     567                        if ( empty( $form_field['slug'] ) ) {
     568                                continue;
     569                        }
     570                        $id = ! empty( $form_field['ID'] ) ? $form_field['ID'] : $field_id;
     571                        $values[ strtolower( $form_field['slug'] ) ] = isset( $answers[ $id ] ) ? $answers[ $id ] : '';
     572                }
     573                foreach ( (array) $visitor as $key => $value ) {
     574                        $key = strtolower( $key );
     575                        if ( '' !== (string) $value && ( ! isset( $values[ $key ] ) || '' === $values[ $key ] ) ) {
     576                                $values[ $key ] = (string) $value;
     577                        }
     578                }
     579                if ( empty( $values ) ) {
     580                        return $field;
     581                }
     582
     583                $search  = array();
     584                $replace = array();
     585                foreach ( $values as $slug => $value ) {
     586                        $search[]  = '%' . $slug . '%';
     587                        // Escaped because the chatbot renders these texts as HTML.
     588                        $replace[] = esc_html( $value );
     589                }
     590
     591                foreach ( array( 'label', 'caption' ) as $key ) {
     592                        if ( isset( $field[ $key ] ) && is_string( $field[ $key ] ) ) {
     593                                $field[ $key ] = str_ireplace( $search, $replace, $field[ $key ] );
     594                        }
     595                }
     596                if ( isset( $field['config']['default'] ) && is_string( $field['config']['default'] ) ) {
     597                        $field['config']['default'] = str_ireplace( $search, $replace, $field['config']['default'] );
     598                }
     599
     600                return $field;
    506601        }
    507602}
  • conversational-forms/trunk/readme.txt

    r3745718 r3754699  
    55Requires at least: 4.6
    66Tested up to: 7.1
    7 Stable tag: 1.5.2
     7Stable tag: 1.5.3
    88Requires PHP: 5.6
    99License: GPLv2 or later
    … …  
    144144== Changelog ==
    145145
     146= 1.5.3 =
     147# Fixed a form issue
     148
    146149= 1.5.2 =
    147150# Improved Security
Note: See TracChangeset for help on using the changeset viewer.