| @@ -11,52 +11,90 @@ | ||
| 11 | 11 | * @var FrmFieldSelectionData|null |
| 12 | 12 | */ |
| 13 | 13 | private static $field_selection_data; |
| 14 | 14 | |
| 15 | + /** | |
| 16 | + * Render the fields the form builder asked for over ajax. | |
| 17 | + * | |
| 18 | + * The browser sends field ids only. The fields themselves are read from the form, which is one | |
| 19 | + * indexed query shared with the rest of the request through the field cache, and cheaper than | |
| 20 | + * shipping every field's data down to the page and straight back up again. | |
| 21 | + * | |
| 22 | + * @return void | |
| 23 | + */ | |
| 15 | 24 | public static function load_field() { |
| 16 | 25 | FrmAppHelper::permission_check( 'frm_edit_forms' ); |
| 17 | 26 | check_ajax_referer( 'frm_ajax', 'nonce' ); |
| 18 | 27 | |
| 19 | - // Javascript may be included in some field settings. | |
| 20 | - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized | |
| 21 | - $fields = isset( $_POST['field'] ) ? wp_unslash( $_POST['field'] ) : array(); | |
| 28 | + $field_ids = FrmAppHelper::get_post_param( 'field_ids', array(), 'absint' ); | |
| 29 | + $form_id = FrmAppHelper::get_post_param( 'form_id', 0, 'absint' ); | |
| 22 | 30 | |
| 23 | - if ( ! $fields ) { | |
| 31 | + if ( ! $form_id || ! is_array( $field_ids ) || ! $field_ids ) { | |
| 24 | 32 | wp_die(); |
| 25 | 33 | } |
| 26 | 34 | |
| 27 | 35 | $_GET['page'] = 'formidable'; |
| 28 | - | |
| 29 | - $values = array( | |
| 30 | - 'id' => FrmAppHelper::get_post_param( 'form_id', '', 'absint' ), | |
| 36 | + $fields = self::get_builder_fields_by_id( $form_id ); | |
| 37 | + $values = array( | |
| 38 | + 'id' => $form_id, | |
| 31 | 39 | 'doing_ajax' => true, |
| 32 | 40 | ); |
| 33 | - $field_html = array(); | |
| 41 | + $field_html = array(); | |
| 34 | 42 | |
| 35 | - foreach ( $fields as $field ) { | |
| 36 | - $field = htmlspecialchars_decode( nl2br( $field ) ); | |
| 37 | - $field = json_decode( $field ); | |
| 38 | - | |
| 39 | - if ( ! isset( $field->id ) || ! is_numeric( $field->id ) ) { | |
| 40 | - // This field may have already been loaded | |
| 43 | + foreach ( $field_ids as $field_id ) { | |
| 44 | + if ( ! isset( $fields[ $field_id ] ) ) { | |
| 45 | + // This field may have already been loaded, or is no longer in the form. | |
| 41 | 46 | continue; |
| 42 | 47 | } |
| 43 | 48 | |
| 44 | - if ( ! isset( $field->value ) ) { | |
| 45 | - $field->value = ''; | |
| 46 | - } | |
| 47 | - $field->field_options = json_decode( json_encode( $field->field_options ), true ); | |
| 48 | - $field->options = json_decode( json_encode( $field->options ), true ); | |
| 49 | - $field->default_value = json_decode( json_encode( $field->default_value ), true ); | |
| 49 | + $field = $fields[ $field_id ]; | |
| 50 | 50 | |
| 51 | 51 | ob_start(); |
| 52 | 52 | self::load_single_field( $field, $values ); |
| 53 | - $field_html[ absint( $field->id ) ] = ob_get_clean(); | |
| 53 | + | |
| 54 | + $field_html[ $field_id ] = array( | |
| 55 | + // The type travels with the html so the js can report it to frm_ajax_loaded_field | |
| 56 | + // listeners without a copy of the field. | |
| 57 | + 'type' => $field->type, | |
| 58 | + 'html' => ob_get_clean(), | |
| 59 | + ); | |
| 54 | 60 | }//end foreach |
| 55 | 61 | |
| 56 | 62 | echo json_encode( $field_html ); |
| 57 | 63 | |
| 58 | 64 | wp_die(); |
| 65 | + } | |
| 66 | + | |
| 67 | + /** | |
| 68 | + * Get a form's fields, as the form builder sees them, indexed by field id. | |
| 69 | + * | |
| 70 | + * @since 6.35 | |
| 71 | + * | |
| 72 | + * @param int $form_id | |
| 73 | + * | |
| 74 | + * @return array Field objects keyed by field id. Empty if the form is gone. | |
| 75 | + */ | |
| 76 | + private static function get_builder_fields_by_id( $form_id ) { | |
| 77 | + $form = FrmForm::getOne( $form_id ); | |
| 78 | + | |
| 79 | + if ( ! $form ) { | |
| 80 | + return array(); | |
| 81 | + } | |
| 82 | + | |
| 83 | + $fields = FrmField::get_all_for_form( $form_id ); | |
| 84 | + | |
| 85 | + /** This filter is documented in classes/controllers/FrmFormsController.php */ | |
| 86 | + $fields = apply_filters( 'frm_fields_in_form_builder', $fields, compact( 'form' ) ); | |
| 87 | + | |
| 88 | + $fields_by_id = array(); | |
| 89 | + | |
| 90 | + foreach ( (array) $fields as $field ) { | |
| 91 | + if ( is_object( $field ) && ! empty( $field->id ) ) { | |
| 92 | + $fields_by_id[ (int) $field->id ] = $field; | |
| 93 | + } | |
| 94 | + } | |
| 95 | + | |
| 96 | + return $fields_by_id; | |
| 59 | 97 | } |
| 60 | 98 | |
| 61 | 99 | /** |
| 62 | 100 | * Create a new field with ajax |