plugin updates

This commit is contained in:
Tony Volpe
2024-11-20 22:40:39 -05:00
parent 0238f0c4ca
commit 3362947c6e
434 changed files with 13405 additions and 9202 deletions

View File

@@ -16,10 +16,15 @@ class GFFormDisplay {
const ON_PAGE_RENDER = 1;
const ON_CONDITIONAL_LOGIC = 2;
const SUBMISSION_INITIATED_BY_WEBFORM = 1;
const SUBMISSION_INITIATED_BY_API = 2;
const SUBMISSION_INITIATED_BY_WEBFORM = 1;
const SUBMISSION_INITIATED_BY_API = 2;
const SUBMISSION_INITIATED_BY_API_VALIDATION = 3;
const SUBMISSION_METHOD_POSTBACK = 'postback';
const SUBMISSION_METHOD_AJAX = 'ajax';
const SUBMISSION_METHOD_IFRAME = 'iframe';
const SUBMISSION_METHOD_CUSTOM = 'custom';
/**
* Starting point for the form submission process. Handles the following tasks: Form validation, save for later logic, entry creation, notification and confirmation.
*
@@ -521,11 +526,16 @@ class GFFormDisplay {
$field_value .= '|' . $price;
}
if ( $index % 10 == 0 ) { //hack to skip numbers ending in 0. so that 5.1 doesn't conflict with 5.10
$index ++;
// Checkboxes for Choice fields already have a unique ID.
if ( $field->has_persistent_choices() ) {
$input_id = $field->get_input_id_from_choice_key( $choice['key'] );
$fields[ $input_id ] = wp_hash( GFFormsModel::maybe_trim_input( $field_value, $form['id'], $field ) );
} else {
if ( $index % 10 == 0 ) { //hack to skip numbers ending in 0. so that 5.1 doesn't conflict with 5.10
$index ++;
}
$fields[ $field->id . '.' . $index ++ ] = wp_hash( GFFormsModel::maybe_trim_input( $field_value, $form['id'], $field ) );
}
$fields[ $field->id . '.' . $index ++ ] = wp_hash( GFFormsModel::maybe_trim_input( $field_value, $form['id'], $field ) );
}
break;
case 'consent':
@@ -806,12 +816,17 @@ class GFFormDisplay {
public static function get_form_theme_slug( $form ) {
$form = (array) $form;
$slug = '';
// If form is legacy, return that early to avoid calculating orbital styles.
if ( GFCommon::is_legacy_markup_enabled( $form ) ) {
$slug = 'legacy';
} elseif ( GFCommon::is_preview() ) {
return 'gravity-theme';
} elseif ( is_admin() || GFCommon::is_preview() ) {
$slug = 'gravity-theme';
} elseif ( GFCommon::is_form_editor() ) {
$slug = 'orbital';
} elseif ( isset( $form['theme'] ) ) {
$slug = $form['theme'];
} else {
$instance = rgar( $form, 'page_instance', 0 );
$all_blocks = apply_filters( 'gform_form_block_attribute_values', array() );
@@ -820,18 +835,16 @@ class GFFormDisplay {
// If a theme is selected for this block or shortcode, return that.
if ( isset( $block_settings['theme'] ) ) {
$slug = $block_settings['theme'];
} elseif( rgar( $form, 'theme' ) ) {
$slug = $form['theme'];
}
}
// allow using the short version in shortcodes.
if ( !empty( $slug ) && $slug == 'gravity' ) {
if ( $slug == 'gravity' ) {
$slug = 'gravity-theme';
}
if ( empty( $slug ) || ! in_array( $slug, array( 'legacy', 'gravity-theme', 'orbital' ) ) ) {
$slug = get_option( 'rg_gforms_default_theme', 'gravity-theme' );
$slug = GFForms::get_default_theme();
}
/**
@@ -847,6 +860,42 @@ class GFFormDisplay {
return apply_filters( 'gform_form_theme_slug', $slug, $form );
}
/**
* Determines which themes to enqueue based on the form context and current page.
*
* @since 2.9
*
* @param array $form Current form object.
* @param string|array $field_types Optional. If specified, only load themes for forms with fields of these types. Can be a string with a single field type or an array of strings with multiple field types.
*
* @return array|string[] Returns an array of theme slugs to enqueue
*/
public static function get_themes_to_enqueue( $form, $field_types = '' ) {
// In the form editor, enqueue the theme framework.
if ( GFCommon::is_form_editor() ) {
return array( 'orbital' );
}
// Enqueues Gravity and Theme Framework themes in the block editor
if ( GFCommon::is_block_editor_page() ) {
return array( 'gravity-theme', 'orbital' );
}
// Enqueues Gravity theme in the entry detail views
if ( GFCommon::is_entry_detail() ) {
return array( 'gravity-theme' );
}
// On pages other than the form editor and block editor, if a field type is specified, only enqueue a theme if the form has the specified field type.
if ( ! empty( $field_types ) && ! GFCommon::get_fields_by_type( $form, $field_types ) ) {
return array();
}
$selected_theme = self::get_form_theme_slug( $form );
return array( $selected_theme );
}
/**
* Fire the post render events for a form instance when the form is visible on the page.
*
@@ -880,6 +929,7 @@ class GFFormDisplay {
postRenderFired = true;
jQuery( document ).trigger( \'gform_post_render\', [' . $form_id . ', ' . $current_page . '] );
gform.utils.trigger( { event: \'gform/postRender\', native: false, data: { formId: ' . $form_id . ', currentPage: ' . $current_page . ' } } );
gform.utils.trigger( { event: \'gform/post_render\', native: false, data: { formId: ' . $form_id . ', currentPage: ' . $current_page . ' } } );
if ( visibilityTestDiv ) {
visibilityTestDiv.parentNode.removeChild( visibilityTestDiv );
}
@@ -958,10 +1008,20 @@ class GFFormDisplay {
*/
$form_args = apply_filters( 'gform_form_args', compact( 'form_id', 'display_title', 'display_description', 'force_display', 'field_values', 'ajax', 'tabindex' ) );
// The submission_method property can be set in the gform_form_args filter to specify how the form should be submitted.
// Supported values are: self::SUBMISSION_METHOD_AJAX (for true ajax submission), self::SUBMISSION_METHOD_POSTBACK (for standard form submission), self::SUBMISSION_METHOD_IFRAME (for the legacy iframe-based ajax submission ) or self::SUBMISSION_METHOD_CUSTOM (for submissions that will be triggered by third party code. i.e. Stripe Add-On).
// It is optional, but if set, will override the $ajax property.
if ( isset( $form_args['submission_method'] ) ) {
$form_args['ajax'] = $form_args['submission_method'] == self::SUBMISSION_METHOD_IFRAME;
} elseif ( $form_args ) {
$form_args['submission_method'] = $form_args['ajax'] ? self::SUBMISSION_METHOD_IFRAME : self::SUBMISSION_METHOD_POSTBACK;
}
if ( empty( $form_args['form_id'] ) ) {
return self::get_form_not_found_html( $form_id, $ajax );
}
// phpcs:ignore
extract( $form_args );
//looking up form id by form name
@@ -986,6 +1046,9 @@ class GFFormDisplay {
$form['page_instance'] = self::$processed[ $form_id ];
self::$processed[ $form_id ]++;
// Setting form style and theme
$form = self::set_form_styles( $form, $style_settings, $form_theme );
$action = remove_query_arg( 'gf_token' );
if ( rgpost( 'gform_send_resume_link' ) == $form_id ) {
@@ -1070,7 +1133,7 @@ class GFFormDisplay {
}
$is_postback = true;
$is_valid = rgar( $submission_info, 'is_valid' ) || rgar( $submission_info, 'is_confirmation' );
$form = $submission_info['form'];
$form = self::set_form_styles( $submission_info['form'], $style_settings, $form_theme );
$lead = $submission_info['lead'];
$confirmation_message = rgget( 'confirmation_message', $submission_info );
@@ -1100,14 +1163,8 @@ class GFFormDisplay {
}
}
if ( null !== $form_theme ) {
$form['theme'] = $form_theme;
}
$form['styles'] = self::get_form_styles( $style_settings );
//Fired right before the form rendering process. Allow users to manipulate the form object before it gets displayed in the front end
$form = gf_apply_filters( array( 'gform_pre_render', $form_id ), $form, $ajax, $field_values );
// Running $form through the gform_pre_render filter.
$form = self::gform_pre_render( $form, 'form_display', $ajax, $field_values );
if ( empty( $form ) ) {
return self::get_form_not_found_html( $form_id, $ajax );
@@ -1272,11 +1329,9 @@ class GFFormDisplay {
</div>';
}
$action = esc_url( $action );
$action = esc_url( $action );
$form_string .= gf_apply_filters( array( 'gform_form_tag', $form_id ), "<form method='post' enctype='multipart/form-data' {$target} id='gform_{$form_id}' {$form_css_class} action='{$action}' data-formid='{$form_id}' novalidate>", $form );
// If Save and Continue token was provided but expired/invalid, display error message.
if ( isset( $_GET['gf_token'] ) && ! is_array( $incomplete_submission_info ) ) {
@@ -1329,7 +1384,7 @@ class GFFormDisplay {
$style = self::is_page_active( $form_id, 1 ) ? '' : "style='display:none;'";
$class = ' ' . rgar( $form, 'firstPageCssClass', '' );
$class = esc_attr( $class );
$form_string .= "<div id='gform_page_{$form_id}_1' class='gform_page{$class}' data-js='page-field-id-1' {$style}>
$form_string .= "<div id='gform_page_{$form_id}_1' class='gform_page{$class}' data-js='page-field-id-0' {$style}>
<div class='gform_page_fields'>";
}
@@ -1367,6 +1422,8 @@ class GFFormDisplay {
}
$form_string .= "</{$tag}>";
$label_placement = rgar( $form, 'labelPlacement', 'top_label' );
if ( $has_pages ) {
$last_page_button = rgar( $form, 'lastPageButton', array() );
$previous_button_alt = rgar( $last_page_button, 'imageAlt', __( 'Previous Page', 'gravityforms' ) );
@@ -1380,16 +1437,15 @@ class GFFormDisplay {
* @param array $form The Form object to filter through
*/
$previous_button = gf_apply_filters( array( 'gform_previous_button', $form_id ), $previous_button, $form );
$form_string .= '</div>' . self::gform_footer( $form, 'gform_page_footer ' . rgar( $form, 'labelPlacement', 'before' ), $ajax, $field_values, $previous_button, $display_title, $display_description, $tabindex, $form_theme, $style_settings ) . '
$form_string .= '</div>' . self::gform_footer( $form, 'gform-page-footer gform_page_footer ' . $label_placement, $ajax, $field_values, $previous_button, $display_title, $display_description, $tabindex, $form_theme, $style_settings, $submission_method ) . '
</div>'; //closes gform_page
}
$form_string .= '</div>'; //closes gform_body
//suppress form footer for multi-page forms (footer will be included on the last page
$label_placement = rgar( $form, 'labelPlacement', 'before' );
if ( ! $has_pages ) {
$form_string .= self::gform_footer( $form, 'gform_footer ' . $label_placement, $ajax, $field_values, '', $display_title, $display_description, $tabindex, $form_theme, $style_settings );
$form_string .= self::gform_footer( $form, 'gform-footer gform_footer ' . $label_placement, $ajax, $field_values, '', $display_title, $display_description, $tabindex, $form_theme, $style_settings, $submission_method );
}
$form_string .= '
@@ -1637,27 +1693,12 @@ class GFFormDisplay {
public static function get_form_button( $form_id, $button_input_id, $button, $default_text, $class, $alt, $target_page_number, $onclick = '' ) {
$is_form_editor = GFCommon::is_form_editor();
$tabindex = GFCommon::get_tabindex();
$input_type = ( rgar( $button, 'type' ) === 'link' ) ? 'button' : 'submit';
$input_onclick = $is_form_editor ? '' : "onclick='gform.submission.handleButtonClick(this)'";
$tabindex = GFCommon::get_tabindex();
$input_type = ( rgar( $button, 'type' ) === 'link' ) ? 'button' : 'submit';
$do_submit = "jQuery(\"#gform_{$form_id}\").trigger(\"submit\",[true]);";
if( $is_form_editor ) {
$onclick = '';
} else {
if ( ! empty( $target_page_number ) ) {
$onclick = "onclick='jQuery(\"#gform_target_page_number_{$form_id}\").val(\"{$target_page_number}\"); {$onclick} {$do_submit} ' onkeypress='if( event.keyCode == 13 ){ jQuery(\"#gform_target_page_number_{$form_id}\").val(\"{$target_page_number}\"); {$onclick} {$do_submit} } '";
$input_type = 'button';
} else {
// prevent multiple form submissions when button is pressed multiple times
$set_submitting = "if( !jQuery(\"#gform_{$form_id}\")[0].checkValidity || jQuery(\"#gform_{$form_id}\")[0].checkValidity()){window[\"gf_submitting_{$form_id}\"]=true;}";
$onclick_submit = $button['type'] == 'link' ? $do_submit : '';
$onclick = "onclick='if(window[\"gf_submitting_{$form_id}\"]){return false;} {$set_submitting} {$onclick} {$onclick_submit}' onkeypress='if( event.keyCode == 13 ){ if(window[\"gf_submitting_{$form_id}\"]){return false;} {$set_submitting} {$onclick} {$do_submit} }'";
}
if ( ! empty( $target_page_number ) ) {
$input_type = 'button';
}
if ( rgar( $button, 'type' ) == 'text' || rgar( $button, 'type' ) == 'link' || empty( $button['imageUrl'] ) ) {
@@ -1673,16 +1714,16 @@ class GFFormDisplay {
$target = '';
$icon = '<svg aria-hidden="true" focusable="false" width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M0 8a4 4 0 004 4h3v3a1 1 0 102 0v-3h3a4 4 0 100-8 4 4 0 10-8 0 4 4 0 00-4 4zm9 4H7V7.414L5.707 8.707a1 1 0 01-1.414-1.414l3-3a1 1 0 011.414 0l3 3a1 1 0 01-1.414 1.414L9 7.414V12z" fill="#6B7280"/></svg>';
}
$button_input = "<{$tag} type='{$input_type}' {$target} id='{$button_input_id}_link' class='{$class}' {$tabindex} {$onclick}>{$icon} {$button_text}</{$tag}>";
$button_input = "<{$tag} type='{$input_type}' {$target} id='{$button_input_id}_link' {$input_onclick} class='{$class}' {$tabindex} >{$icon} {$button_text}</{$tag}>";
} else {
$class .= GFFormDisplay::get_submit_button_class( $button, $form_id );
$button_input = "<input type='{$input_type}' id='{$button_input_id}' class='{$class}' value='" . esc_attr( $button_text ) . "' {$tabindex} {$onclick} />";
$button_input = "<input type='{$input_type}' id='{$button_input_id}' class='{$class}' {$input_onclick} value='" . esc_attr( $button_text ) . "' {$tabindex} />";
}
} else {
$imageUrl = esc_url( $button['imageUrl'] );
$class .= GFFormDisplay::get_submit_button_class( $button, $form_id );
$class .= ' gform_image_button';
$button_input = "<input type='image' src='{$imageUrl}' id='{$button_input_id}' class='{$class}' alt='{$alt}' {$tabindex} {$onclick} />";
$button_input = "<input type='image' src='{$imageUrl}' id='{$button_input_id}' {$input_onclick} class='{$class}' alt='{$alt}' {$tabindex} />";
}
return $button_input;
@@ -1699,7 +1740,7 @@ class GFFormDisplay {
* @return string The CSS class(es) for this button.
*/
public static function get_submit_button_class( $button, $form_id ) {
$class = ( GFCommon::is_form_editor() ) ? '' : ' button';
$class = ' button';
$class .= rgar( $button, 'width' ) && 'full' == $button['width'] ? ' gform-button--width-full' : '';
// if the button is at the bottom, and if it has width, add a width class.
@@ -1712,7 +1753,7 @@ class GFFormDisplay {
return $class;
}
public static function gform_footer( $form, $class, $ajax, $field_values, $previous_button, $display_title, $display_description, $tabindex = 1, $theme = null, $style_settings = null ) {
public static function gform_footer( $form, $class, $ajax, $field_values, $previous_button, $display_title, $display_description, $tabindex = 1, $theme = null, $style_settings = null, $submission_method = self::SUBMISSION_METHOD_POSTBACK ) {
$form_id = absint( $form['id'] );
$footer = "
<div class='" . esc_attr( $class ) . "'>";
@@ -1747,7 +1788,9 @@ class GFFormDisplay {
if ( ! empty( $style_settings ) ) {
$valid_json = json_decode( $style_settings );
if ( null !== $valid_json ) {
$style_settings .= "&amp;styles={$style_settings}";
$style_settings = "&amp;styles={$style_settings}";
} else {
$style_settings = '';
}
}
@@ -1777,14 +1820,15 @@ class GFFormDisplay {
}
$unique_id = isset( self::$submission[ $form_id ] ) && rgar( self::$submission[ $form_id ], 'resuming_incomplete_submission' ) == true ? rgar( GFFormsModel::$unique_ids, $form_id ) : GFFormsModel::get_form_unique_id( $form_id );
$footer .= "
$footer .= "
<input type='hidden' class='gform_hidden' name='gform_submission_method' data-js='gform_submission_method_{$form_id}' value='{$submission_method}' />
<input type='hidden' class='gform_hidden' name='is_submit_{$form_id}' value='1' />
<input type='hidden' class='gform_hidden' name='gform_submit' value='{$form_id}' />
{$save_inputs}
<input type='hidden' class='gform_hidden' name='gform_unique_id' value='" . esc_attr( $unique_id ) . "' />
<input type='hidden' class='gform_hidden' name='state_{$form_id}' value='" . self::get_state( $form, $field_values ) . "' />
<input type='hidden' class='gform_hidden' name='gform_target_page_number_{$form_id}' id='gform_target_page_number_{$form_id}' value='" . esc_attr( $next_page ) . "' />
<input type='hidden' class='gform_hidden' name='gform_source_page_number_{$form_id}' id='gform_source_page_number_{$form_id}' value='" . esc_attr( $current_page ) . "' />
<input type='hidden' autocomplete='off' class='gform_hidden' name='gform_target_page_number_{$form_id}' id='gform_target_page_number_{$form_id}' value='" . esc_attr( $next_page ) . "' />
<input type='hidden' autocomplete='off' class='gform_hidden' name='gform_source_page_number_{$form_id}' id='gform_source_page_number_{$form_id}' value='" . esc_attr( $current_page ) . "' />
<input type='hidden' name='gform_field_values' value='" . esc_attr( $field_values_str ) . "' />
{$files_input}
</div>";
@@ -2751,10 +2795,12 @@ class GFFormDisplay {
foreach ( $found_forms as $form_id => $attributes ) {
$form = GFAPI::get_form( $form_id );
$ajax = $attributes['ajax'];
$theme = $attributes['theme'];
$form['theme'] = ! rgempty( 'theme', $attributes ) ? $attributes['theme'] : GFForms::get_default_theme();
unset( $attributes['theme'] ); // removing theme from styles for consistency. $form['theme'] should be used instead.
$form['styles'] = self::get_form_styles( $attributes );
if ( $form && $form['is_active'] && ! $form['is_trash'] ) {
self::enqueue_form_scripts( $form, $ajax, $theme );
self::enqueue_form_scripts( $form, $ajax, $form['theme'] );
}
}
@@ -2837,11 +2883,11 @@ class GFFormDisplay {
// Get the form ID and AJAX attributes.
$form_id = (int) $block['attrs']['formId'];
$ajax = isset( $block['attrs']['ajax'] ) ? (bool) $block['attrs']['ajax'] : false;
$theme = isset( $block['attrs']['theme'] ) ? $block['attrs']['theme'] : '';
if ( self::is_applicable_form( $form_id, $ajax, $found_forms ) ) {
$found_forms[ $form_id ] = $block['attrs'];
$found_forms[ $form_id ]['ajax'] = $ajax;
$found_forms[ $form_id ]['theme'] = $theme;
$found_forms[ $form_id ]['theme'] = isset( $found_forms[ $form_id ]['theme'] ) ? $found_forms[ $form_id ]['theme'] : '';
}
}
}
@@ -2901,11 +2947,11 @@ class GFFormDisplay {
$form_id = (int) $form_id;
$ajax = isset( $attr['ajax'] ) && strtolower( substr( $attr['ajax'], 0, 4 ) ) == 'true';
$theme = isset( $attr['theme'] ) ? $attr['theme'] : '';
$styles = json_decode( rgar( $attr, 'styles' ), true ) ? json_decode( rgar( $attr, 'styles' ), true ) : array();
if ( self::is_applicable_form( $form_id, $ajax, $found_forms ) ) {
$found_forms[ $form_id ]['ajax'] = $ajax;
$found_forms[ $form_id ]['theme'] = $theme;
$found_forms[ $form_id ] = $styles;
$found_forms[ $form_id ]['ajax'] = $ajax;
$found_forms[ $form_id ]['theme'] = rgar( $attr, 'theme' );
}
}
}
@@ -3006,44 +3052,15 @@ class GFFormDisplay {
if ( is_rtl() ) {
$assets[] = new GF_Style_Asset( 'gforms_rtl_css' );
}
}
} else {
$theme_slug = $theme ? $theme : self::get_form_theme_slug( $form );
if ( 'gravity-theme' == $theme_slug ) {
$assets[] = new GF_Style_Asset( 'gform_basic' );
/**
* Allows users to disable the main theme.css file from being loaded on the Front End.
*
* @since 2.5-beta-3
*
* @param boolean Whether to disable the theme css.
*/
$disable_theme_css = apply_filters( 'gform_disable_form_theme_css', false );
if ( ! $disable_theme_css ) {
$assets[] = new GF_Style_Asset( 'gform_theme' );
}
}
if ( 'orbital' == $theme_slug ) {
$assets[] = new GF_Style_Asset( 'gravity_forms_orbital_theme' );
$assets[] = new GF_Style_Asset( 'gravity_forms_theme_foundation' );
$assets[] = new GF_Style_Asset( 'gravity_forms_theme_framework' );
$assets[] = new GF_Style_Asset( 'gravity_forms_theme_reset' );
}
// Theme related styles will be enqueued by the theme layer process initiated by form-display/block-styles/block-styles-handler.php
}
if ( self::has_password_visibility( $form ) ) {
$assets[] = new GF_Style_Asset( 'dashicons' );
}
}
$assets[] = new GF_Script_Asset( 'wp-a11y' );
@@ -3260,6 +3277,27 @@ class GFFormDisplay {
gf_do_action( array( 'gform_print_scripts', $form['id'] ), $form, $ajax );
}
/**
* Check if a form has any Image Choice fields.
*
* @since 2.8
*
* @param $form
*
* @return mixed|null
*/
public static function has_image_choices( $form ) {
$has_image_choices = GFAPI::get_fields_by_type( $form, 'image_choice' ) ? true : false;
/**
* A filter to determine if a form has image choices.
*
* @param bool $has_image_choices True or False if there are any image choice fields in the form
* @param array $form The Current form object
*/
return apply_filters( 'gform_has_image_choices', $has_image_choices, $form );
}
public static function has_conditional_logic( $form ) {
$has_conditional_logic = self::has_conditional_logic_legwork( $form );
@@ -4168,13 +4206,11 @@ class GFFormDisplay {
* @return string|string[]|void
*/
public static function get_field( $field, $value = '', $force_frontend_label = false, $form = null, $field_values = null ) {
$is_form_editor = GFCommon::is_form_editor();
$is_form_editor = GFCommon::is_form_editor();
$is_entry_detail = GFCommon::is_entry_detail();
$is_admin = $is_form_editor || $is_entry_detail;
$custom_class = $is_admin ? esc_attr( $field->cssClass ) : esc_attr( self::convert_legacy_class( $form, $field->cssClass ) );
$form_id = (int) rgar( $form, 'id' );
$is_admin = $is_form_editor || $is_entry_detail;
$custom_class = $is_admin ? esc_attr( $field->cssClass ) : esc_attr( self::convert_legacy_class( $form, $field->cssClass ) );
$form_id = (int) rgar( $form, 'id' );
if ( $field->type == 'page' ) {
if ( $is_entry_detail ) {
@@ -4204,14 +4240,13 @@ class GFFormDisplay {
$save_button = apply_filters( 'gform_savecontinue_link', $save_button, $form );
$save_button = apply_filters( "gform_savecontinue_link_{$form_id}", $save_button, $form );
$style = self::is_page_active( $form_id, $field->pageNumber ) ? '' : "style='display:none;'";
$custom_class = ! empty( $custom_class ) ? " {$custom_class}" : '';
$label_placement = rgar( $form, 'labelPlacement', 'top_label' );
$tag = GFCommon::is_legacy_markup_enabled( $form ) ? 'ul' : 'div';
$label_placement = rgar( $form, 'labelPlacement', 'before' );
$html = "</{$tag}>
</div>
<div class='gform_page_footer {$label_placement}'>
<div class='gform-page-footer gform_page_footer {$label_placement}'>
{$previous_button} {$next_button} {$save_button}
</div>
</div>
@@ -4242,13 +4277,22 @@ class GFFormDisplay {
$selectable_class = $is_admin ? 'selectable' : '';
$hidden_class = in_array( $input_type, array( 'hidden', 'hiddenproduct' ) ) ? 'gform_hidden' : '';
$choice_input_type_class = $field->type === 'checkbox' || $field->type === 'radio' || $field->type === 'consent' || ( isset( $field->inputType ) && ( $field->inputType === 'checkbox' || $field->inputType === 'radio' || $field->inputType === 'consent' ) ) ? 'gfield--type-choice' : '';
$choice_fields = array( 'checkbox', 'radio', 'consent' );
$choice_input_type_class = in_array( $field->type, $choice_fields ) || ( isset( $field->inputType ) && in_array( $field->inputType, $choice_fields ) ) ? 'gfield--type-choice' : '';
$choice_alignment_class = $field->type === 'multi_choice' ? 'gfield--choice-align-' . GF_Field_Multiple_Choice::get_field_choice_alignment( $field ) : '';
$choice_input_image_shape_class = '';
$choice_input_image_style_class = '';
if ( $field->type === 'image_choice' ) {
$choice_input_image_shape_class = isset( $form['styles'] ) && rgar( $form['styles'], 'inputImageChoiceAppearance' ) ? 'gfield--image-choice-appearance-' . $form['styles']['inputImageChoiceAppearance'] : 'gfield--image-choice-appearance-card';
$choice_input_image_style_class = isset( $form['styles'] ) && rgar( $form['styles'], 'inputImageChoiceStyle' ) ? 'gfield--image-choice-style-' . $form['styles']['inputImageChoiceStyle'] : 'gfield--image-choice-style-square';
}
$field_input_type_class = isset( $field->inputType ) && ! empty( $field->inputType ) ? sprintf( 'gfield--input-type-%s', $field->inputType ) : '';
$field_specific_class = $field->get_field_css_class();
$section_class = $field->type == 'section' ? 'gsection' : '';
$page_class = $field->type == 'page' ? 'gpage' : '';
$page_class = $field->type == 'page' ? 'gpage gform-theme__disable' : '';
$html_block_class = $field->type == 'html' ? 'gfield_html' : '';
$html_formatted_class = $field->type == 'html' && ! $field->disableMargins ? 'gfield_html_formatted' : '';
$html_no_follows_desc_class = $field->type == 'html' && ! $is_admin && ! self::prev_field_has_description( $form, $field->id ) ? 'gfield_no_follows_desc' : '';
@@ -4287,7 +4331,7 @@ class GFFormDisplay {
$span_class = $field->get_css_grid_class( $form );
$css_class = "gfield gfield--type-{$field->type} $choice_input_type_class $field_input_type_class $field_specific_class $selectable_class $span_class $error_class $section_class $admin_only_class $custom_class $hidden_class $html_block_class $html_formatted_class $html_no_follows_desc_class $option_class $quantity_class $product_class $total_class $donation_class $shipping_class $page_class $required_class $hidden_product_class $creditcard_warning_class $submit_width_class $calculation_class $sublabel_class $has_description_class $description_class $label_placement $validation_class $visibility_class $admin_hidden_class";
$css_class = "gfield gfield--type-{$field->type} $choice_input_type_class $choice_input_image_shape_class $choice_input_image_style_class $field_input_type_class $field_specific_class $selectable_class $span_class $error_class $section_class $admin_only_class $custom_class $hidden_class $html_block_class $html_formatted_class $html_no_follows_desc_class $option_class $quantity_class $product_class $total_class $donation_class $shipping_class $page_class $required_class $hidden_product_class $creditcard_warning_class $submit_width_class $calculation_class $sublabel_class $has_description_class $description_class $label_placement $validation_class $visibility_class $admin_hidden_class $choice_alignment_class";
$css_class = preg_replace( '/\s+/', ' ', $css_class ); // removing extra spaces
/*
@@ -4388,7 +4432,7 @@ class GFFormDisplay {
$field_content = gf_apply_filters( array( 'gform_field_content', $form_id, $field->id ), $field_content, $field, $value, 0, $form_id );
$admin_compact_view_menu = $is_form_editor ? sprintf( "<div id='dropdown_field_%s' data-js='gform-compact-view-overflow-menu' class='gform-compact-view-overflow-menu'></div>", $field->id ) : '';
$admin_compact_view_menu = $is_form_editor ? sprintf( "<div id='dropdown_field_%s' data-js='gform-compact-view-overflow-menu' class='gform-compact-view-overflow-menu gform-theme__disable'></div>", $field->id ) : '';
if( $is_form_editor ) {
$field_content = '<div class="gfield-admin-wrapper">' . $field_content . '</div>' . ( $field->type !== 'submit' ? $admin_compact_view_menu : '' );
@@ -4404,7 +4448,7 @@ class GFFormDisplay {
$page_count = self::get_max_page_number( $form );
$current_page = $page;
$page_name = rgars( $form['pagination'], sprintf( 'pages/%d', $current_page - 1 ) );
$page_name = ! empty( $page_name ) ? ' - ' . $page_name : '';
$page_name = ! empty( $page_name ) ? " - " . $page_name : '';
$style = $form['pagination']['style'];
$color = $style == 'custom' ? " color:{$form['pagination']['color']};" : '';
$bgcolor = $style == 'custom' ? " background-color:{$form['pagination']['backgroundColor']};" : '';
@@ -4434,7 +4478,7 @@ class GFFormDisplay {
$progress_bar .= "
<div id='gf_progressbar_wrapper_{$form_id}' class='gf_progressbar_wrapper' data-start-at-zero='{$start_at_zero}'>
{$progress_bar_title_open}";
$progress_bar .= ! $progress_complete ? esc_html__( 'Step', 'gravityforms' ) . " <span class='gf_step_current_page'>{$current_page}</span> " . esc_html__( 'of', 'gravityforms' ) . " <span class='gf_step_page_count'>{$page_count}</span>{$page_name}" : "{$page_name}";
$progress_bar .= ! $progress_complete ? esc_html__( 'Step', 'gravityforms' ) . " <span class='gf_step_current_page'>{$current_page}</span> " . esc_html__( 'of', 'gravityforms' ) . " <span class='gf_step_page_count'>{$page_count}</span><span class='gf_step_page_name'>{$page_name}</span>" : "{$page_name}";
$progress_bar .= "
{$progress_bar_title_close}
<div class='gf_progressbar gf_progressbar_{$style}' aria-hidden='true'>
@@ -4484,7 +4528,6 @@ class GFFormDisplay {
$progress_steps .= "</div>";
/**
* Filter the multi-page progress steps markup.
*
@@ -4704,7 +4747,7 @@ class GFFormDisplay {
public static function replace_save_variables( $text, $form, $resume_token, $email = null ) {
$resume_token = sanitize_key( $resume_token );
$form_id = intval( $form['id'] );
$page_url = rgpost( 'current_page_url' ) ? sanitize_url( rawurldecode( rgpost( 'current_page_url' ) ) ): GFFormsModel::get_current_page_url();
/**
* Filters the 'Save and Continue' URL to be used with a partial entry submission.
*
@@ -4715,7 +4758,7 @@ class GFFormDisplay {
* @param string $resume_token The token that is used within the URL.
* @param string $email The email address associated with the partial entry.
*/
$resume_url = apply_filters( 'gform_save_and_continue_resume_url', add_query_arg( array( 'gf_token' => $resume_token ), GFFormsModel::get_current_page_url() ), $form, $resume_token, $email );
$resume_url = apply_filters( 'gform_save_and_continue_resume_url', add_query_arg( array( 'gf_token' => $resume_token ), $page_url ), $form, $resume_token, $email );
$resume_url = esc_url( $resume_url );
$resume_link = "<a href=\"{$resume_url}\" class='resume_form_link'>{$resume_url}</a>";
$text = str_replace( '{save_link}', $resume_link, $text );
@@ -4753,9 +4796,10 @@ class GFFormDisplay {
$action = esc_url( remove_query_arg( 'gf_token' ) );
$ajax = isset( $_POST['gform_ajax'] );
$anchor = self::get_anchor( $form, $ajax );
$action .= $anchor['id'];
$submission_method = rgpost( 'gform_submission_method' );
$is_iframe_ajax = $submission_method == self::SUBMISSION_METHOD_IFRAME;
$anchor = self::get_anchor( $form, $is_iframe_ajax );
$action .= $anchor['id'];
$resume_token = esc_attr( $resume_token );
@@ -4769,31 +4813,30 @@ class GFFormDisplay {
$nonce_input = wp_nonce_field( 'gform_send_resume_link', '_gform_send_resume_link_nonce', true, false );
}
$target = $ajax ? "target='gform_ajax_frame_{$form_id}'" : '';
$target = $is_iframe_ajax ? "target='gform_ajax_frame_{$form_id}'" : '';
$ajax_fields = '';
if ( $ajax ) {
$ajax_fields = "<input type='hidden' name='gform_ajax' value='" . esc_attr( "form_id={$form_id}&amp;title=1&amp;description=1&amp;tabindex=1" ) . "' />";
$ajax_fields .= "<input type='hidden' name='gform_field_values' value='' />";
// Adding inputs required by GFFormDisplay::is_submit_form_id_valid() to verify the Ajax submission should be processed.
$ajax_fields .= "<input type='hidden' class='gform_hidden' name='is_submit_{$form_id}' value='1' />";
$ajax_fields .= "<input type='hidden' class='gform_hidden' name='gform_submit' value='{$form_id}' />";
$iframe_ajax_fields = '';
if ( $is_iframe_ajax ) {
$iframe_ajax_fields = "<input type='hidden' name='gform_ajax' value='" . esc_attr( "form_id={$form_id}&amp;title=1&amp;description=1&amp;tabindex=1" ) . "' />";
$iframe_ajax_fields .= "<input type='hidden' name='gform_field_values' value='' />";
}
$ajax_submit = $ajax ? "onclick='jQuery(\"#gform_{$form_id}\").trigger(\"submit\",[true]);'" : '';
$form_submission_inputs = "<input type='hidden' class='gform_hidden' name='gform_submission_method' data-js='gform_submission_method_{$form_id}' value='{$submission_method}' />
<input type='hidden' class='gform_hidden' name='is_submit_{$form_id}' value='1' />
<input type='hidden' class='gform_hidden' name='gform_submit' value='{$form_id}' />";
$ajax_submit = $is_iframe_ajax ? "onclick='jQuery(\"#gform_{$form_id}\").trigger(\"submit\",[true]);'" : '';
if ( GFCommon::is_legacy_markup_enabled( $form ) ) {
$resume_form = "<div class='form_saved_message_emailform'>
<form action='{$action}' method='POST' id='gform_{$form_id}' data-formid='{$form_id}' {$target}>
{$ajax_fields}
{$iframe_ajax_fields}
<label for='gform_resume_email' class='gform_resume_email_label gfield_label' aria-describedby='email-validation-error'>{$email_input_label}</label>
<input type='email' name='gform_resume_email' value='{$email_esc}' id='gform_resume_email' placeholder='{$email_input_label}' aria-describedby='email-validation-error'/>
<input type='hidden' name='gform_resume_token' value='{$resume_token}' />
<input type='hidden' name='gform_send_resume_link' value='{$form_id}' />
<input type='hidden' class='gform_hidden' name='is_submit_{$form_id}' value='1' />
<input type='hidden' class='gform_hidden' name='gform_submit' value='{$form_id}' />
<input type='submit' name='gform_send_resume_link_button' id='gform_send_resume_link_button_{$form_id}' value='{$resume_submit_button_text}' {$ajax_submit}/>
{$form_submission_inputs}
<input type='submit' name='gform_send_resume_link_button' id='gform_send_resume_link_button_{$form_id}' onclick='gform.submission.handleButtonClick(this)' value='{$resume_submit_button_text}' {$ajax_submit}/>
{$validation_output}
{$nonce_input}
</form>
@@ -4803,7 +4846,7 @@ class GFFormDisplay {
<form action='{$action}' method='POST' id='gform_{$form_id}' data-formid='{$form_id}' {$target}>
<div class='gform-body gform_body'>
<div id='gform_fields_{$form_id}' class='gform_fields top_label form_sublabel_below description_below'>
{$ajax_fields}
{$iframe_ajax_fields}
<div class='gfield gfield--type-email gfield--width-full field_sublabel_below field_description_below gfield_visibility_visible'>
<label for='gform_resume_email' class='gform_resume_email_label gfield_label gform-field-label'>{$email_input_label}{$email_input_label_required}</label>
<div class='ginput_container ginput_container_text'>
@@ -4813,12 +4856,11 @@ class GFFormDisplay {
</div>
</div>
</div>
<div class='gform_footer top_label'>
<div class='gform-footer gform_footer top_label'>
<input type='hidden' name='gform_resume_token' value='{$resume_token}' />
<input type='hidden' name='gform_send_resume_link' value='{$form_id}' />
<input type='hidden' class='gform_hidden' name='is_submit_{$form_id}' value='1' />
<input type='hidden' class='gform_hidden' name='gform_submit' value='{$form_id}' />
<input type='submit' name='gform_send_resume_link_button' id='gform_send_resume_link_button_{$form_id}' value='{$resume_submit_button_text}' {$ajax_submit}/>
{$form_submission_inputs}
<input type='submit' name='gform_send_resume_link_button' id='gform_send_resume_link_button_{$form_id}' onclick='gform.submission.handleButtonClick(this)' value='{$resume_submit_button_text}' {$ajax_submit}/>
{$nonce_input}
</div>
</form>
@@ -4835,7 +4877,7 @@ class GFFormDisplay {
* @return bool
*/
$always_show_spinner = gf_apply_filters( array( 'gform_always_show_spinner', $form_id ), true );
if ( ! $ajax && $always_show_spinner ) {
if ( ! $is_iframe_ajax && $always_show_spinner ) {
$default_spinner = GFCommon::get_base_url() . '/images/spinner.svg';
$spinner_url = gf_apply_filters( array( 'gform_ajax_spinner_url', $form_id ), $default_spinner, $form );
$theme_slug = self::get_form_theme_slug( $form );
@@ -5275,6 +5317,9 @@ class GFFormDisplay {
'inputBackgroundColor',
'inputColor',
'inputPrimaryColor',
'inputImageChoiceAppearance',
'inputImageChoiceStyle',
'inputImageChoiceSize',
'labelFontSize',
'labelColor',
'descriptionFontSize',
@@ -5302,7 +5347,7 @@ class GFFormDisplay {
* @return array|false|string
*/
public static function get_form_styles( $style_settings ) {
$global_styles = self::validate_form_styles( apply_filters( 'gform_default_styles', false ) );
$global_styles = apply_filters( 'gform_default_styles', false );
$form_styles = '';
@@ -5323,7 +5368,35 @@ class GFFormDisplay {
$form_styles = $global_styles;
}
return $form_styles;
return self::validate_form_styles( $form_styles );
}
/**
* Applies the form styles and form theme to the form object so that the proper style block is rendered to the page.
*
* @since 2.9.0
*
* @param mixed $form The form object.
* @param string|null $style_settings Style settings for the form. This will either come from the shortcode or from the block editor settings.
* @param string|null $form_theme The theme selected for the form. This will either come from the shortcode or from the block editor settings.
* @return array Returns the form object with the 'styles' and 'theme' properties set.
*/
private static function set_form_styles( $form, $style_settings, $form_theme ) {
if ( $style_settings === false ) {
// Form styles specifically set to false disables inline form css styles.
$form_styles = false;
} else {
$form_styles = !empty( $style_settings ) ? json_decode( $style_settings, true ) : array();
}
// Removing theme from styles for consistency. $form['theme'] should be used instead.
if ( $form_styles ) {
unset( $form_styles['theme'] );
}
$form['styles'] = self::get_form_styles( $form_styles );
$form['theme'] = ! empty( $form_theme ) ? $form_theme : GFForms::get_default_theme();
return $form;
}
/**
@@ -5356,4 +5429,58 @@ class GFFormDisplay {
return $spacer;
}
/**
* @var array Cached forms that have been filtered by the gform_pre_render filter.
*/
private static $cached_forms = array();
/**
* Filters the $form object through the gform_pre_render filter and caches the result so that this filter is only triggered once per request.
*
* @since 2.9.0
*
* @param array $form The form object being filtered.
* @param string $context The context that the method is being called in. Possible values are 'form_display' and 'form_config'.
* @param bool|null $ajax Whether the form is being displayed via AJAX. Only used when $context is 'form_display'.
* @param array|null $field_values The field values to be used to populate the form. Only used when $context is 'form_display'.
*
* @return array Returns the form object after being filtered by the gform_pre_render filter.
*/
public static function gform_pre_render( $form, $context, $ajax = null, $field_values = null ) {
$cache_key = $form['id'] . '_' . $context;
if ( ! isset( self::$cached_forms[ $cache_key ] ) ) {
/**
* Fired right before the form rendering process. Allow users to manipulate the form object before it gets displayed in the front end.
*
* @since 2.9.0 Added the $context parameter.
*
* @param array $form The form object being filtered.
* @param bool|null $ajax Whether the form is being displayed via AJAX. Only used when $context is 'form_display'.
* @param array|null $field_values The field values to be used to populate the form. Only used when $context is 'form_display'.
* @param string $context The context that the method is being called in. Possible values are 'form_display' and 'form_config'.
*/
self::$cached_forms[ $cache_key ] = gf_apply_filters( array( 'gform_pre_render', $form['id'] ), $form, $ajax, $field_values, $context );
}
return self::$cached_forms[ $cache_key ];
}
/**
* Flushes the forms cached by the gform_pre_render method.
*
* @since 2.9.0
*
* @param string $cache_key The cache key to flush. The format is FORM-ID_CONTEXT. Defaults to null and if not provided, all cached forms will be flushed.
*
* @return void
*/
public static function flush_cached_forms( $cache_key = null ) {
if ( $cache_key ) {
unset( self::$cached_forms[ $cache_key ] );
} else {
self::$cached_forms = array();
}
}
}