form = $form; $this->entry = $entry; $this->step = $step; $this->display_empty_fields = $display_empty_fields; $this->_is_dynamic_conditional_logic_enabled = $this->is_dynamic_conditional_logic_enabled(); $this->_editable_fields = $step->get_editable_fields(); } /** * Renders the form. Uses GFFormDisplay::get_form() to display the fields. */ public function render_edit_form() { $this->add_hooks(); // Impersonate front-end form. unset( $_GET['page'] ); require_once( GFCommon::get_base_path() . '/form_display.php' ); $html = GFFormDisplay::get_form( $this->form['id'], false, false, true, $this->entry ); $this->remove_hooks(); echo $html; } /** * Add the filters and actions required to modify the form markup for this step. */ public function add_hooks() { add_filter( 'gform_pre_render', array( $this, 'filter_gform_pre_render' ), 999 ); add_filter( 'gform_submit_button', '__return_empty_string' ); add_filter( 'gform_disable_view_counter', '__return_true' ); add_filter( 'gform_field_input', array( $this, 'filter_gform_field_input' ), 10, 2 ); add_filter( 'gform_form_tag', '__return_empty_string' ); add_filter( 'gform_get_form_filter', array( $this, 'filter_gform_get_form_filter' ) ); add_filter( 'gform_field_container', array( $this, 'filter_gform_field_container' ), 10, 2 ); add_filter( 'gform_has_conditional_logic', array( $this, 'filter_gform_has_conditional_logic' ), 10, 2 ); add_filter( 'gform_field_css_class', array( $this, 'filter_gform_field_css_class' ), 10, 2 ); add_action( 'gform_register_init_scripts', array( $this, 'deregsiter_init_scripts' ), 11 ); } /** * Remove the filters and actions. */ public function remove_hooks() { remove_filter( 'gform_pre_render', array( $this, 'filter_gform_pre_render' ), 999 ); remove_filter( 'gform_submit_button', '__return_empty_string' ); remove_filter( 'gform_disable_view_counter', '__return_true' ); remove_filter( 'gform_field_input', array( $this, 'filter_gform_field_input' ), 10 ); remove_filter( 'gform_form_tag', '__return_empty_string' ); remove_filter( 'gform_get_form_filter', array( $this, 'filter_gform_get_form_filter' ) ); remove_filter( 'gform_field_container', array( $this, 'filter_gform_field_container' ), 10 ); remove_filter( 'gform_has_conditional_logic', array( $this, 'filter_gform_has_conditional_logic' ), 10 ); remove_filter( 'gform_field_css_class', array( $this, 'filter_gform_field_css_class' ), 10 ); remove_action( 'gform_register_init_scripts', array( $this, 'deregsiter_init_scripts' ), 11 ); } /** * Target of the gform_pre_render filter. * Removes the page fields from the form. * * @param array $form The current form. * * @return array The filtered form. */ public function filter_gform_pre_render( $form ) { if( $form['id'] != rgget( 'id' ) ) { return $form; } $form = $this->remove_page_fields( $form ); $fields = array(); $dynamic_conditional_logic_enabled = $this->_is_dynamic_conditional_logic_enabled; /** * Process all other field types. * * @var GF_Field $field */ foreach ( $form['fields'] as $field ) { if ( $field->type == 'section' ) { // Unneeded section fields will be removed via filter_gform_field_container(). $field->adminOnly = false; $fields[] = $field; continue; } if ( $dynamic_conditional_logic_enabled ) { $conditional_logic_fields = GFFormDisplay::get_conditional_logic_fields( $form, $field->id ); $field->conditionalLogicFields = $conditional_logic_fields; } $field->gravityflow_is_display_field = $this->is_display_field( $field ); // Remove unneeded fields from the form to prevent JS errors resulting from scripts expecting fields to be present and visible. if ( $this->can_remove_field( $field ) ) { continue; } $is_product_field = GFCommon::is_product_field( $field->type ); if ( ! $this->has_product_fields && $is_product_field ) { $this->has_product_fields = true; } if ( ! $this->is_editable_field( $field ) ) { $content = $this->get_non_editable_field( $field ); if ( empty( $content ) ) { continue; } $this->_non_editable_field_content[ $field->id ] = $content; $this->_non_editable_field_script_names[] = $field->type . '_' . $field->id; if ( $field->type == 'tos' ) { $field->gwtermsofservice_require_scroll = false; } $field->description = null; $field->maxLength = null; } else { $field->gravityflow_is_editable = true; if ( ! $this->_has_editable_product_field && $is_product_field && $field->type != 'total' ) { $this->_has_editable_product_field = true; } } if ( empty( $field->label ) ) { $field->label = $field->adminLabel; } $field->adminOnly = false; $field->adminLabel = ''; if ( $field->type === 'hidden' ) { // Render hidden fields as text fields. $field = new GF_Field_Text( $field ); $field->type = 'text'; } $fields[] = $field; } $form['fields'] = $fields; $this->_modified_form = $form; return $form; } /** * Removes the form button logic and page fields so they are not taken into account when processing conditional logic for other fields. * Also disables save and continue. * * @param array $form The form currently being processed. * * @return array */ public function remove_page_fields( $form ) { unset( $form['save'] ); unset( $form['button']['conditionalLogic'] ); $dynamic_conditional_logic_enabled = $this->_is_dynamic_conditional_logic_enabled; /* @var GF_Field $field */ foreach ( $form['fields'] as $key => $field ) { if ( $field->type == 'page' ) { unset( $form['fields'][ $key ] ); continue; } $is_applicable_field = $this->is_editable_field( $field ); if ( $is_applicable_field && $field->has_calculation() ) { $this->set_calculation_dependencies( $field->calculationFormula ); } if ( ! $is_applicable_field ) { // Populate the $_display_fields array. $is_applicable_field = $this->is_display_field( $field, true ); } if ( ! $dynamic_conditional_logic_enabled || ! $is_applicable_field ) { // Clear the field conditional logic properties as conditional logic is not enabled for the step or the field is not for display or editable. $field->conditionalLogicFields = null; $field->conditionalLogic = null; } } return $form; } /** * Add the IDs of any fields in the formula to the $_calculation_dependencies array. * * @since 1.7.1-dev * * @param string $formula The calculation formula to be evaluated. */ public function set_calculation_dependencies( $formula ) { if ( empty( $formula ) ) { return; } preg_match_all( '/{[^{]*?:(\d+).*?}/mi', $formula, $matches, PREG_SET_ORDER ); if ( ! empty( $matches ) ) { foreach ( $matches as $match ) { $field_id = rgar( $match, 1 ); if ( $field_id && ! $this->is_calculation_dependency( $field_id ) ) { $this->_calculation_dependencies[] = $field_id; } } } } /** * Checks whether a field is required for calculations. * * @since 1.7.1-dev * * @param GF_Field|string $field The field object or field ID to be checked. * * @return bool */ public function is_calculation_dependency( $field ) { $field_id = is_object( $field ) ? $field->id : $field; return in_array( $field_id, $this->_calculation_dependencies ); } /** * Determines if the field can be removed from the form object. * * Fields involved in conditional logic must always be added to the form. * * @param GF_Field $field The current field. * * @return bool */ public function can_remove_field( $field ) { $can_remove_field = ! ( $this->is_editable_field( $field ) || $this->is_display_field( $field ) || $this->is_calculation_dependency( $field ) ) && empty( $field->conditionalLogicFields ); return $can_remove_field; } /** * Target for the gform_field_input filter. * * Handles the construction of the field input. Returns markup for the editable field or the display value. * * @param string $html The field input markup. * @param GF_Field $field The current field. * * @return string */ public function filter_gform_field_input( $html, $field ) { if ( ! $this->is_editable_field( $field ) ) { return rgar( $this->_non_editable_field_content, $field->id ); } if ( ! empty( $html ) ) { // the field input has already been set via the gform_field_input filter. e.g. the Signature Add-On < v3. return $html; } $posted_form_id = rgpost( 'gravityflow_submit' ); if ( $posted_form_id == $this->form['id'] && rgpost( 'step_id' ) == $this->step->get_id() ) { // Updated or failed validation. $value = GFFormsModel::get_field_value( $field ); } else { $value = GFFormsModel::get_lead_field_value( $this->entry, $field ); if ( $field->get_input_type() == 'email' && $field->emailConfirmEnabled ) { $_POST[ 'input_' . $field->id . '_2' ] = $value; } if ( $field->get_input_type() == 'multiselect' && $field->storageType === 'json' ) { $value = json_decode( $value, true ); } } if ( $field->get_input_type() == 'fileupload' ) { $field->_is_entry_detail = true; } $value = apply_filters( 'gravityflow_field_value_entry_editor', $value, $field, $this->form, $this->entry, $this->step ); $value = $this->get_post_image_value( $value, $field ); $value = $this->get_post_category_value( $value, $field ); $html = $field->get_field_input( $this->form, $value, $this->entry ); $html .= $this->maybe_get_coupon_script( $field ); if ( $field->type === 'chainedselect' && function_exists( 'gf_chained_selects' ) ) { if ( ! wp_script_is( 'gform_chained_selects' ) ) { wp_enqueue_script( 'gform_chained_selects' ); gf_chained_selects()->localize_scripts(); } if ( ! $this->_is_dynamic_conditional_logic_enabled && wp_script_is( 'gform_conditional_logic' ) ) { $script = "if ( typeof window.gf_form_conditional_logic === 'undefined' ) { window.gf_form_conditional_logic = []; }"; GFFormDisplay::add_init_script( $field->formId, 'conditional_logic', GFFormDisplay::ON_PAGE_RENDER, $script ); } } return $html; } /** * Ensures the post image field value is in the correct format for populating the field. * * @since 2.1.2-dev * * @param string|array $value The field value. * @param GF_Field $field The current field object. * * @return string|array */ public function get_post_image_value( $value, $field ) { if ( $field->type !== 'post_image' || empty( $value ) || ! is_string( $value ) || strpos( $value, '|:|' ) === false ) { return $value; } $array = explode( '|:|', $value ); $value = array( $field->id . '.1' => rgar( $array, 1 ), // Title. $field->id . '.4' => rgar( $array, 2 ), // Caption. $field->id . '.7' => rgar( $array, 3 ), // Description. ); $path_info = pathinfo( rgar( $array, 0 ) ); if ( ! isset( GFFormsModel::$uploaded_files[ $field->formId ]["input_{$field->id}"] ) ) { GFFormsModel::$uploaded_files[ $field->formId ]["input_{$field->id}"] = $path_info['basename']; } return $value; } /** * Ensures the post category field value is in the correct format for populating the field. * * @since 2.1.1-dev * * @param string|array $value The field value. * @param GF_Field $field The current field object. * * @return string|array */ public function get_post_category_value( $value, $field ) { if ( $field->type !== 'post_category' || empty( $value ) ) { return $value; } if ( is_array( $value ) ) { foreach ( $value as $key => $item ) { if ( ! empty( $item ) ) { $value[ $key ] = $this->get_post_category_id( $item ); } } } else { $value = $this->get_post_category_id( $value ); } return $value; } /** * Returns the post category id from the supplied value. * * The entry value will be in the format "category_name:category_id". * * @since 2.1.1-dev * * @param string $value The field value. * * @return string */ public function get_post_category_id( $value ) { $parts = explode( ':', $value ); return isset( $parts[1] ) ? $parts[1] : $parts[0]; } /** * Get the gform_product_total script for the coupon field when there aren't any editable product fields. * * @param GF_Field $field The field currently being processed. * * @return string */ public function maybe_get_coupon_script( $field ) { if ( $field->type != 'coupon' || $this->_has_editable_product_field ) { return ''; } $total = GFCommon::get_order_total( $this->form, $this->entry ); return ""; } /** * Checks whether dynamic conditional logic is enabled. * * @return bool */ public function is_dynamic_conditional_logic_enabled() { return $this->step && $this->step->conditional_logic_editable_fields_enabled && $this->step->conditional_logic_editable_fields_mode != 'page_load' && gravity_flow()->fields_have_conditional_logic( $this->form ); } /** * Target for the gform_get_form_filter filter. * Strips the closing form tag and replaces the Gravity Forms token for Gravity Flow's token. * * @param string $form_string The form markup. * * @return string */ public function filter_gform_get_form_filter( $form_string ) { $form_string = str_replace( 'gform_submit', 'gravityflow_submit', $form_string ); $form_string = str_replace( '', '', $form_string ); return $form_string; } /** * Generates and returns the markup for a display field. * * @param GF_Field $field The current field object. * * @return string */ public function get_non_editable_field( $field ) { if ( $field->type == 'html' ) { $html = GFCommon::replace_variables( $field->content, $this->form, $this->entry, false, true, false, 'html' ); $html = do_shortcode( $html ); return $html; } $html = ''; $value = RGFormsModel::get_lead_field_value( $this->entry, $field ); $conditional_logic_dependency = $this->_is_dynamic_conditional_logic_enabled && ! empty( $field->conditionalLogicFields ); if ( $conditional_logic_dependency || $this->is_calculation_dependency( $field ) ) { $html = $field->get_field_input( $this->form, $value, $this->entry ); } if ( ! $this->is_display_field( $field ) ) { return $html; } if ( $html ) { $html = '
'; } $value = $this->maybe_get_product_calculation_value( $value, $field ); $input_type = $field->get_input_type(); if ( $input_type == 'hiddenproduct' ) { $display_value = $value[ $field->id . '.2' ]; } else { $display_value = GFCommon::get_lead_field_display( $field, $value, $this->entry['currency'] ); } $display_value = apply_filters( 'gform_entry_field_value', $display_value, $field, $this->entry, $this->form ); if ( $this->display_empty_fields ) { if ( empty( $display_value ) || $display_value === '0' ) { $display_value = ' '; } $display_value = sprintf( '