diff --git a/gravity-forms/gw-random-fields.php b/gravity-forms/gw-random-fields.php index 4e4f7cd59..9e814f557 100644 --- a/gravity-forms/gw-random-fields.php +++ b/gravity-forms/gw-random-fields.php @@ -5,7 +5,7 @@ * * Randomly display a specified number of fields on your form. * - * @version 1.3 + * @version 1.3.x * @author David Smith * @license GPL-2.0+ * @link https://gravitywiz.com/random-fields-with-gravity-forms/ @@ -14,30 +14,28 @@ * Plugin URI: https://gravitywiz.com/random-fields-with-gravity-forms/ * Description: Randomly display a specified number of fields on your form. * Author: Gravity Wiz - * Version: 1.3 + * Version: 1.3.x * Author URI: http://gravitywiz.com */ class GFRandomFields { - public $all_random_field_ids; public $display_count; public $selected_field_ids = array(); public $preserve_order; - - public function __construct( $form_id, $display_count = 5, $random_field_ids = false, $preserve_order = false ) { + public $restructure_pages; + public function __construct( $form_id, $display_count = 5, $random_field_ids = false, $preserve_order = false, $restructure_pages = true ) { $this->_form_id = (int) $form_id; $this->all_random_field_ids = array_map( 'intval', array_filter( (array) $random_field_ids ) ); $this->display_count = (int) $display_count; $this->preserve_order = (bool) $preserve_order; + $this->restructure_pages = (bool) $restructure_pages; add_filter( "gform_pre_render_$form_id", array( $this, 'pre_render' ) ); + add_filter( "gform_pre_process_$form_id", array( $this, 'pre_render' ) ); + add_filter( "gform_form_tag_$form_id", array( $this, 'store_selected_field_ids' ), 10, 2 ); - add_filter( "gform_validation_$form_id", array( $this, 'validate' ) ); - add_filter( "gform_pre_submission_filter_$form_id", array( $this, 'pre_render' ) ); - add_filter( "gform_target_page_{$form_id}", array( $this, 'modify_target_page' ), 10, 3 ); - //add_filter( "gform_admin_pre_render_$form_id", array( $this, 'admin_pre_render' ) ); add_action( 'gform_entry_created', array( $this, 'save_selected_field_ids_meta' ), 10, 2 ); } @@ -46,15 +44,6 @@ public function pre_render( $form ) { return $this->filter_form_fields( $form, $this->get_selected_field_ids( false, $form ) ); } - public function admin_pre_render( $form ) { - - if ( (int) $form['id'] !== $this->_form_id ) { - return $form; - } - - return $form; - } - public function store_selected_field_ids( $form_tag, $form ) { $hash = $this->get_selected_field_ids_hash( $form ); $value = implode( ',', $this->get_selected_field_ids( false, $form ) ); @@ -78,12 +67,21 @@ public function validate( $validation_result ) { public function filter_form_fields( $form, $selected_fields ) { + // Prevent the form from being randomized multiple times. Once per runtime is enough. 😅 + if ( rgar( $form, 'gwrfRandomized' ) ) { + return $form; + } + $filtered_fields = array(); - $selected_fields = array_map( 'intval', $selected_fields ); $random_indexes = array(); + $selected_fields = array_map( 'intval', $selected_fields ); foreach ( $form['fields'] as $field ) { + if ( $this->restructure_pages && $field->type === 'page' ) { + continue; + } + if ( in_array( (int) $field['id'], $this->get_random_field_ids( $form['fields'] ), true ) ) { if ( in_array( (int) $field['id'], $selected_fields, true ) ) { $filtered_fields[] = $field; @@ -94,7 +92,13 @@ public function filter_form_fields( $form, $selected_fields ) { } } - if ( ! $this->preserve_order ) { + if ( $this->restructure_pages && GFCommon::has_pages( $form ) ) { + $filtered_fields = $this->handle_pagination( $filtered_fields, $form ); + } + + // @todo Fix issue where randomized order is not preserved on subsequent form renders (e.g. pages, validation errors). + // For now, let's preserve field order for paginated forms. + if ( ! $this->preserve_order && ( ! $this->restructure_pages || ! GFCommon::has_pages( $form ) ) ) { $reordered_fields = array(); $random_index_key = $random_indexes; @@ -114,11 +118,69 @@ public function filter_form_fields( $form, $selected_fields ) { } - $form['fields'] = $filtered_fields; + $form['gwrfRandomized'] = true; + $form['fields'] = $filtered_fields; return $form; } + public function handle_pagination( $filtered_fields, $form ) { + + // Remove Section fields if they are the only fields left on a page after randomization. + foreach ( $filtered_fields as $index => $field ) { + if ( $field->type === 'section' && $this->is_only_field_on_page( $filtered_fields, $field ) ) { + unset( $filtered_fields[ $index ] ); + } + } + + $filtered_fields = array_filter( $filtered_fields ); + + // Get all the page numbers that exist now that the fields have been filtered. + $page_numbers = array_values( array_unique( wp_list_pluck( $filtered_fields, 'pageNumber' ) ) ); + + // Updates 0-based index to a 1-based index so the array key represents the new page number exactly (e.g. Page 1 vs Page 0). + array_unshift( $page_numbers, false ); + unset( $page_numbers[0] ); + + // Loop through the page numbers and assign fields to their new page number based on their old page number. + foreach ( $page_numbers as $new_page_number => $old_page_number ) { + + // $pages is still a 0-based indexed array; subtract 2 from the old page number to find the right page. + $page = $this->get_page_by_page_number( $form, $old_page_number ); + if ( ! $page ) { + continue; + } + + foreach ( $filtered_fields as $index => &$field ) { + if ( $field->pageNumber == $old_page_number && $field->type !== 'page' ) { + + $field->pageNumber = $new_page_number; + + // Only inject the page once. + if ( isset( $page ) ) { + // Update the page to its new page number and inject it into the filtered fields ahead. + $page->pageNumber = $new_page_number; + array_splice( $filtered_fields, $index, 0, array( $page ) ); + unset( $page ); + } + } + } + } + + unset( $field ); + + return $filtered_fields; + } + + public function get_page_by_page_number( $form, $page_number ) { + foreach ( $form['fields'] as $field ) { + if ( $field->type === 'page' && $field->pageNumber == $page_number ) { + return $field; + } + } + return false; + } + public function get_random_field_ids( $fields ) { if ( ! empty( $this->all_random_field_ids ) ) { @@ -179,49 +241,13 @@ public function save_selected_field_ids_meta( $entry, $form ) { } } - /** - * When the form is submitted modify the target page to avoid navigating to a page that has no visible fields. - * - * Known limitation: If the first page of a form has no visible fields, this will not avoid that as this only works - * on submission. - * - * @param $page_number - * @param $form - * @param $current_page - * @param $field_values - * - * @return int|mixed - */ - public function modify_target_page( $page_number, $form, $current_page ) { - - $page_number = intval( $page_number ); - $form = $this->pre_render( $form ); - - $page_has_visible_fields = false; - foreach ( $form['fields'] as $field ) { - if ( (int) $field->pageNumber === (int) $page_number && GFFormDisplay::is_field_validation_supported( $field ) ) { - $page_has_visible_fields = true; - } - } - - if ( ! $page_has_visible_fields ) { - // Are we moving to the next or previous page? - $is_next = $page_number === 0 || $page_number > $current_page; - $max_page = GFFormDisplay::get_max_page_number( $form ); - if ( $page_number !== 0 && $page_number < $max_page ) { - $page_number += $is_next ? 1 : -1; - // When moving to a previous page, always stop at the first page. Otherwise, we'll submit the form. - if ( ! $is_next && $page_number === 0 ) { - return 1; - } - $page_number = $this->modify_target_page( $page_number, $form, $current_page ); - } elseif ( $page_number >= $max_page ) { - // Target page number 0 to submit the form. - $page_number = 0; + public function is_only_field_on_page( $fields, $source_field ) { + foreach ( $fields as $_field ) { + if ( $_field->id != $source_field->id && $_field->pageNumber == $source_field->pageNumber && $_field->type !== 'section' ) { + return false; } } - - return $page_number; + return true; } }