Current File : //proc/self/cwd/wp-content/plugins/gravityforms/includes/fields/class-gf-field-repeater.php |
<?php
if ( ! class_exists( 'GFForms' ) ) {
die();
}
/**
* The Repeater field.
*
* 2.4
*
* Class GF_Field_Repeater
*/
class GF_Field_Repeater extends GF_Field {
public $type = 'repeater';
/**
* Returns the field title for the form editor.
*
* @since 2.4
*
* @return string
*/
public function get_form_editor_field_title() {
return esc_attr__( 'Repeater', 'gravityforms' );
}
/**
* Returns the field settings for the form editor.
*
* @since 2.4
*
* @return array
*/
function get_form_editor_field_settings() {
return array(
'conditional_logic_field_setting',
'error_message_setting',
'label_setting',
'label_placement_setting',
'admin_label_setting',
'rules_setting',
'visibility_setting',
'description_setting',
'css_class_setting',
);
}
public function get_form_editor_button() {
return array();
}
/**
* Used to determine the required validation result.
*
* @since 2.4
*
* @param int $form_id The ID of the form currently being processed.
*
* @return bool
*/
public function is_value_submission_empty( $form_id ) {
return false;
}
/**
* Validates each sub-field.
*
* @since 2.4
*
* @param string|array $items The field values from get_value_submission().
* @param array $form The Form Object currently being processed.
*/
public function validate( $items, $form ) {
if ( empty( $items ) ) {
return;
}
/* @var GF_Field[] $fields */
$fields = $this->fields;
foreach ( $items as $i => $item ) {
foreach ( $fields as $field ) {
$field->set_context_property( 'itemIndex', $i );
$inputs = $field->get_entry_inputs();
if ( is_array( $inputs ) ) {
$field_value = array();
$field_keys = array_keys( $item );
foreach ( $field_keys as $input_id ) {
if ( is_numeric( $input_id ) && absint( $input_id ) == absint( $field->id ) ) {
$field_value[ $input_id ] = $item[ $input_id ];
}
}
} else {
$field_value = isset( $item[ $field->id ] ) ? $item[ $field->id ] : '';
}
if ( $field->isRequired && $field->is_value_empty( $field_value ) ) {
$field->failed_validation = true;
} else {
$field->validate( $field_value, $form );
}
$custom_validation_result = gf_apply_filters( array( 'gform_field_validation', $form['id'], $field->id ), array(
'is_valid' => $field->failed_validation ? false : true,
'message' => $field->validation_message
), $field_value, $form, $field );
$this->failed_validation = rgar( $custom_validation_result, 'is_valid' ) ? false : true;
// Reset the field validation and item index.
$field->failed_validation = false;
$field->set_context_property( 'itemIndex', null );
if ( $this->failed_validation ) {
// One field has failed validation so the entire repeater fails.
return;
}
}
}
}
/**
* Retrieve the field value on submission.
*
* @since 2.4
*
* @param array $field_values The dynamic population parameter names with their corresponding values to be populated.
* @param bool|true $get_from_post_global_var Whether to get the value from the $_POST array as opposed to $field_values.
*
* @return array|string
*/
public function get_value_submission( $field_values, $get_from_post_global_var = true ) {
$submission_values = $this->get_value_submission_recursive( $field_values, $get_from_post_global_var );
$items = $this->hydrate( $submission_values );
return $items[ $this->id ];
}
/**
* Returns the submission values for the repeater.
*
* @since 2.4
*
* @param array $field_values
* @param bool $get_from_post_global_var
*
* @return array
*/
public function get_value_submission_recursive( $field_values, $get_from_post_global_var ) {
$items = array();
if ( isset( $this->fields ) && is_array( $this->fields ) ) {
foreach ( $this->fields as $sub_field ) {
/* @var GF_Field $sub_field */
if ( isset( $sub_field->fields ) && is_array( $sub_field->fields ) ) {
/* @var GF_Field_Repeater_Table $sub_field */
$field_items = $sub_field->get_value_submission_recursive( $field_values, $get_from_post_global_var );
} else {
$values = $sub_field->get_value_submission( $field_values, $get_from_post_global_var );
if ( is_array( $sub_field->get_entry_inputs() ) ) {
$prefix = '';
} else {
$prefix = $sub_field->id . '_';
}
$field_items = $this->flatten( $values, $prefix, $sub_field->is_value_submission_array() );
}
$items = array_merge( $items, $field_items );
}
} else {
$values = $this->get_value_submission( $field_values, $get_from_post_global_var );
$field_items = $this->flatten( $values, $this->id . '_', $this->is_value_submission_array() );
$items = array_merge( $items, $field_items );
}
return $items;
}
/**
* Utility to flatten array values recursively so they can be saved with the appropriate index.
*
* @since 2.4
*
* @param $array
* @param string $prefix
* @param bool $field_value_is_array
*
* @return array
*/
private function flatten( $array, $prefix = '', $field_value_is_array = false ) {
$result = array();
if ( ! is_array( $array ) ) {
return $result;
}
foreach ( $array as $key => $value ) {
if ( is_array( $value ) ) {
if ( $field_value_is_array && ! is_array( $value[0] ) ) {
$result[ $prefix . $key ] = $value;
} else {
$result = $result + $this->flatten( $value, $prefix . $key . '_' );
}
} else {
$result[ $prefix . $key ] = $value;
}
}
return $result;
}
/**
* Returns the field inner markup.
*
* @since 2.4
*
* @param array $form The Form Object currently being processed.
* @param string|array $values The field values. From default/dynamic population, $_POST, or a resumed incomplete submission.
* @param null|array $entry Null or the Entry Object currently being edited.
*
* @return string
*/
public function get_field_input( $form, $values = '', $entry = null ) {
if ( $this->is_form_editor() ) {
return sprintf( "<p>%s</p>", $this->label );
}
if ( empty( $values ) ) {
$values = array( '' );
}
$input_top = $this->get_input_top( $values );
$items = $this->get_input_items( $values, $entry );
$html = $input_top . $items . $this->get_input_bottom();
$max_items = intval( $this->maxItems );
return sprintf( "<div class='gfield_repeater_wrapper' data-max_items='{$max_items}'>%s</div>", $html );
}
/**
* Returns the markup for the top of the repeater container.
*
* This method must return the opening tag for the container and this tag must have the class 'gfield_repeater_container'
*
* @since 2.4
*
* @param $values
*
* @return string
*/
public function get_input_top( $values ) {
$html = "<fieldset class='gfield_repeater gfield_repeater_container'>\n";
$label = esc_html( $this->label );
$html .= "<legend class='gfield_label'>{$label}</legend>";
return $html;
}
/**
* Returns the markup for the items.
*
* This method must return a single HTML element with the class 'gfield_repeater_items'. This elemment must contain
* all the items as direct children and each item must have the class 'gfield_repeater_item'.
*
* @since 2.4
*
* @param $values
* @param $entry
*
* @return string
*/
public function get_input_items( $values, $entry ) {
/* @var GF_Field[] $fields */
$fields = $this->fields;
$form = GFAPI::get_form( $this->formId );
$rows = '<div class="gfield_repeater_items">';
$i = 0;
foreach ( $values as $value ) {
$row = "<div class='gfield_repeater_item'>";
foreach ( $fields as $field ) {
$field_value = $this->get_field_value( $field, $value );
$field->set_context_property( 'itemIndex', $i );
$field_input = $this->get_sub_field_input( $field, $form, $field_value, $entry, $i );
$row .= "<div class='gfield_repeater_cell'>" . $field_input . '</div>';
$field->set_context_property( 'itemIndex', null );
}
$buttons = $this->get_buttons( $values );
$row .= "<div class='gfield_repeater_buttons'>{$buttons}</div>";
$row .= '</div>';
$rows .= $row;
$i++;
}
$rows .= '</div>';
return $rows;
}
/**
* Return the markup for the bottom of the repeater. Close the tags opened in the top.
*
* @since 2.4
*
* @return string
*/
public function get_input_bottom() {
return '</fieldset>';
}
public function get_field_content( $value, $force_frontend_label, $form ) {
$is_form_editor = $this->is_form_editor();
$is_entry_detail = $this->is_entry_detail();
$is_admin = $is_form_editor || $is_entry_detail;
$admin_buttons = $this->get_admin_buttons();
$description = $this->get_description( $this->description, 'gfield_description' );
if ( $this->is_description_above( $form ) ) {
$clear = $is_admin ? "<div class='gf_clear'></div>" : '';
$field_content = sprintf( "%s%s{FIELD}$clear", $admin_buttons, $description );
} else {
$field_content = sprintf( "%s{FIELD}%s", $admin_buttons, $description );
}
return $field_content;
}
/**
* Returns the repeater buttons.
*
* @since 2.4
*
* @return string
*/
public function get_buttons( $values ) {
$is_form_editor = $this->is_form_editor();
$delete_display = count( $values ) == 1 ? 'visibility:hidden;' : '';
$add_events = $is_form_editor ? '' : "onclick='gformAddRepeaterItem(this)' onkeypress='gformAddRepeaterItem(this)'";
$delete_events = $is_form_editor ? '' : sprintf( "onclick='if(confirm(\"%s\")){gformDeleteRepeaterItem(this)};' onkeypress='gformDeleteRepeaterItem(this)'", esc_js( __( 'Are you sure you want to remove this item?', 'gravityforms' ) ) );
$disabled_icon_class = ! empty( $this->maxItems ) && count( $values ) >= intval( $this->maxItems ) ? 'gfield_icon_disabled' : '';
$add_button_text = $this->addButtonText ? $this->addButtonText : '+';
$remove_button_text = $this->removeButtonText ? $this->removeButtonText : '-' ;
$add_button_class = $this->addButtonText ? 'add_repeater_item_text' : 'add_repeater_item_plus';
$remove_button_class = $this->removeButtonText ? 'remove_repeater_item_text' : 'remove_repeater_item_minus';
$html = "<button type='button' class='add_repeater_item {$disabled_icon_class} {$add_button_class}' {$add_events}>" . $add_button_text . "</button>" .
"<button type='button' class='remove_repeater_item {$remove_button_class}' {$delete_events} style='{$delete_display}'>" . $remove_button_text . "</button>";
return $html;
}
/**
* Gets merge tag values.
*
* @since 2.4
*
* @param array|string $value The value of the input.
* @param string $input_id The input ID to use.
* @param array $entry The Entry Object.
* @param array $form The Form Object
* @param string $modifier The modifier passed.
* @param array|string $raw_value The raw value of the input.
* @param bool $url_encode If the result should be URL encoded.
* @param bool $esc_html If the HTML should be escaped.
* @param string $format The format that the value should be.
* @param bool $nl2br If the nl2br function should be used.
*
* @return string The processed merge tag.
*/
public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {
$use_value = in_array( 'value', $this->get_modifiers() );
$use_text = ! $use_value;
if ( $format == 'html' ) {
$media = $esc_html ? 'screen' :'email';
$merge_tag = $this->get_value_entry_detail( $raw_value, $entry['currency'], $use_text, $format, $media );
} else {
$merge_tag = $this->get_value_export_recursive( $entry, $input_id, $use_text, false, 0, ' ' );
}
return $merge_tag;
}
/**
* Format the entry value safe for displaying on the entry list page.
*
* @since 2.4
*
* @param string $value The field value.
* @param array $entry The Entry Object currently being processed.
* @param string $field_id The field or input ID currently being processed.
* @param array $columns The properties for the columns being displayed on the entry list page.
* @param array $form The Form Object currently being processed.
*
* @return string
*/
public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {
/* translators: %d: the number of items in value of the repeater field. */
$display_value = is_array( $value ) ? sprintf( esc_html__( 'Number of items: %d' ), count( $value ) ) : '';
return $display_value;
}
/**
* Format the entry value safe for displaying on the entry detail page and for the {all_fields} merge tag.
*
* @since 2.4
*
* @param string|array $item_values The field value.
* @param string $currency The entry currency code.
* @param bool|false $use_text When processing choice based fields should the choice text be returned instead of the value.
* @param string $format The format requested for the location the merge is being used. Possible values: html, text or url.
* @param string $media The location where the value will be displayed. Possible values: screen or email.
*
* @return string
*/
public function get_value_entry_detail( $item_values, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
if ( $format == 'text' ) {
return $this->get_value_export_recursive( array( $this->id => $item_values ), $this->id, $use_text, false, 0, ' ' );
}
$repeater_style = $media == 'email' ? "style='padding: 5px 0 0 15px;font-size: 14px'" : '';
$label_style = $media == 'email' ? "style='color: rgba(35, 40, 45, 1.000);font-weight:600; padding-top:10px;font-size: 14px'" : '';
$sub_field_label_style = $media == 'email' ? "style='color:rgb(155, 154, 154);padding-top:8px;font-size: 14px;'" : '';
/* @var GF_Field[] $fields */
$fields = $this->fields;
$html = "<div class='gfield_repeater' {$repeater_style}>";
$repeater_label = $this->nestingLevel === 0 ? '' : $this->label;
$html .= "<div class='gfield_label' {$label_style}>{$repeater_label}</div>";
$html .= '<div class="gfield_repeater_items">';
foreach ( $item_values as $item_value ) {
$html .= '<div class="gfield_repeater_item">';
foreach ( $fields as $sub_field ) {
if ( $sub_field->fields ) {
$sub_field_value = $item_value[ $sub_field->id ];
} else {
$sub_field_value = $this->get_field_value( $sub_field, $item_value );
}
$label = $sub_field->get_field_label( true, $item_values );
$label = empty( $sub_field->fields ) ? "<div class='gfield_repeater_label' {$sub_field_label_style}>{$label}</div>" : '';
$value = $sub_field->get_value_entry_detail( $sub_field_value, $currency, $use_text, 'html', $media );
$value = "<div class='gfield_repeater_value' style='color:rgba(117, 117, 117, 1);font-size: 14px'>{$value}</div>";
$html .= '<div class="gfield_repeater_cell">' . $label . $value . '</div>';
}
$html .= '</div>';
}
$html .= '</div>';
$html .= '</div>';
return $html;
}
/**
* Returns the value for a field inside a repeater.
*
* @since 2.4
*
* @param GF_Field $field
* @param array|string $value
*
* @return array|string
*/
public function get_field_value( $field, $value ) {
if ( $field->fields ) {
$field_value = isset( $value[ $field->id ] ) ? $value[ $field->id ] : '';
} else {
$inputs = $field->get_entry_inputs();
if ( is_array( $value ) ) {
if ( is_array( $inputs ) ) {
$field_value = array();
$field_keys = array_keys( $value );
natsort( $field_keys );
foreach ( $field_keys as $input_id ) {
if ( is_numeric( $input_id ) && absint( $input_id ) == absint( $field->id ) ) {
$val = $value[ $input_id ];
$field_value[ $input_id ] = $val;
}
}
} else {
$field_value = isset( $value[ $field->id ] ) ? $value[ $field->id ] : '';
}
} else {
$field_value = '';
}
}
return $field_value;
}
/**
* Returns the input markup for a field inside a repeater.
*
* Appends the item index to the name and id attributes and validates the value.
*
* @since 2.4
*
* @param GF_Field $field
* @param array $form
* @param array $field_value
* @param array $entry
* @param int $index
*
* @return mixed
*/
public function get_sub_field_input( $field, $form, $field_value, $entry, $index ) {
$field_content = $this->get_sub_field_content( $field, $field_value, $form, $entry );
// Adjust all the name attributes in the markup
preg_match_all( "/(name='input_[^\[|']*)((\[[0-9]*\])*)'/", $field_content, $matches, PREG_SET_ORDER );
$replaced = array();
foreach ( $matches as $match ) {
if ( ! in_array( $match[0], $replaced ) ) {
$input_name = str_replace( $match[1], $match[1] . "[{$index}]", $match[0] );
$field_content = str_replace( $match[0], $input_name, $field_content );
$replaced[] = $match[0];
}
}
// Adjust all the id attributes in the markup
preg_match_all( "/(id='((input|choice)_[0-9|_]*))[0-9|-]*'/", $field_content, $matches, PREG_SET_ORDER );
$replaced = array();
foreach ( $matches as $match ) {
if ( ! in_array( $match[0], $replaced ) ) {
$input_id = str_replace( $match[1], $match[1] . "-{$index}", $match[0] ) ;
$field_content = str_replace( $match[0], $input_id, $field_content );
$replaced[] = $match[0];
}
}
// Adjust all the for attributes in the markup
preg_match_all( "/(for='(input|choice)_[^\[']*)'/", $field_content, $matches, PREG_SET_ORDER );
$replaced = array();
foreach ( $matches as $match ) {
if ( ! in_array( $match[1], $replaced ) ) {
$input_id = $match[1] . "-{$index}";
$field_content = str_replace( $match[1], $input_id, $field_content );
$replaced[] = $match[1];
}
}
$target_page = rgpost( 'gform_target_page_number_' . $this->formId );
$source_page = rgpost( 'gform_source_page_number_' . $this->formId );
$validate = $source_page == $field->pageNumber && rgpost( 'is_submit_' . $this->formId ) && ( $target_page == 0 || $target_page > $source_page );
if ( $validate ) {
$field->failed_validation = false;
if ( $field->isRequired && $field->is_value_empty( $field_value ) ) {
$field->failed_validation = true;
$field->validation_message = empty( $field->errorMessage ) ? __( 'This field is required.', 'gravityforms' ) : $field->errorMessage;
}
if ( ! $field->failed_validation ) {
$field->validate( $field_value, $form );
}
$custom_validation_result = gf_apply_filters( array( 'gform_field_validation', $form['id'], $field->id ), array(
'is_valid' => $field->failed_validation ? false : true,
'message' => $field->validation_message
), $field_value, $form, $field );
$field->failed_validation = rgar( $custom_validation_result, 'is_valid' ) ? false : true;
}
$validation_message = ( $field->failed_validation && ! empty( $field->validation_message ) ) ? sprintf( "<div class='gfield_description validation_message'>%s</div>", $field->validation_message ) : '';
return $field_content . $validation_message;
}
/**
* Returns the markup for the sub field.
*
* @since 2.4
*
* @param GF_Field $field
* @param string|array $value
* @param array $form
* @param array $entry
*
* @return string
*/
public function get_sub_field_content( $field, $value, $form, $entry ) {
$validation_status = $field->failed_validation;
if ( empty( $field->fields ) ) {
// Validation will be handled later inside GF_Field_Repeater::get_sub_field_input so temporarily set failed_validation to false.
$field->failed_validation = false;
}
if ( ! class_exists( 'GFFormDisplay' ) ) {
require_once( GFCommon::get_base_path() .'/form_display.php' );
}
$field_content = GFFormDisplay::get_field_content( $field, $value, true, $form['id'], $form );
$field->failed_validation = $validation_status;
return $field_content;
}
/**
* Builds the repeater's array of items.
*
* @since 2.4
*
* @param $entry
*
* @return mixed
*/
public function hydrate( $entry ) {
$entry[ $this->id ] = $this->get_repeater_items( $entry );
return $entry;
}
/**
* Recursively converts the repeater values from flattened values in the entry array into a multidimensional array
* of items.
*
* @since 2.4
*
* @param array $entry
* @param GF_Field_Repeater $repeater_field
* @param string $index
*
* @return array
*/
public function get_repeater_items( &$entry, $repeater_field = null, $index = '' ) {
if ( ! $repeater_field ) {
$repeater_field = $this;
}
$items = array();
// Blank items are not stored but we need to display them if a value exists with a higher index.
$max_indexes = $this->get_max_indexes( $entry, $repeater_field, $index );
$repeater_fields = array();
foreach ( $repeater_field->fields as $field ) {
if ( is_array( $field->fields ) ) {
$repeater_fields[] = $field;
continue;
}
for ( $i = 0; $i <= $max_indexes[ $field->id ]; $i ++ ) {
$inputs = $field->get_entry_inputs();
if ( is_array( $inputs ) ) {
foreach ( $inputs as $input ) {
$input_id = $input['id'];
$key = $input_id . $index . '_' . $i;
$value = isset( $entry[ $key ] ) ? $entry[ $key ] : '';
// Don't add new item if max indexes is 0 and value is empty.
if ( $field->isRequired || $max_indexes[ $field->id ] > 0 || ( $max_indexes[ $field->id ] === 0 && $value !== '' ) ) {
$items[ $i ][ $input_id ] = $value;
}
if ( isset( $entry[ $key ] ) ) {
unset( $entry[ $key ] );
}
}
} else {
$key = $field->id . $index . '_' . $i;
$value = isset( $entry[ $key ] ) ? $entry[ $key ] : '';
if ( $field->isRequired || $max_indexes[ $field->id ] > 0 || ( $max_indexes[ $field->id ] === 0 && $value !== '' ) ) {
$items[ $i ][ $field->id ] = $value;
}
if ( isset( $entry[ $key ] ) ) {
unset( $entry[ $key ] );
}
}
}
}
if ( ! empty( $repeater_fields ) ) {
$i = 0;
do {
$all_repeaters_have_values = true;
foreach ( $repeater_fields as $repeater ) {
$v = $this->get_repeater_items( $entry, $repeater, $index . '_' . $i );
$is_empty = $this->empty_deep( $v );
if ( ( $i == 0 || ! $is_empty ) || ( empty( $index ) && isset( $items[ $i ] ) && ! $this->empty_deep( $items[ $i ] ) ) ) {
$items[ $i ][ $repeater->id ] = $v;
}
if ( $is_empty ) {
$all_repeaters_have_values = false;
}
}
$i ++;
} while ( $all_repeaters_have_values );
}
return $items;
}
/**
* Parses all the flat entry array keys and returns the maximum index by field ID.
*
* @since 2.4
*
* @param array $entry The entry array
* @param GF_Field_Repeater $repeater_field The repeater field
* @param string $index The index prefix
*
* @return array
*/
protected function get_max_indexes( $entry, $repeater_field, $index ) {
$field_ids = array_keys( $entry );
$max_indexes = array();
$matches = array();
foreach ( $repeater_field->fields as $field ) {
if ( ! isset( $matches[ $field->id ] ) ) {
$matches[ $field->id ] = array( 0 );
}
foreach ( $field_ids as $f_id ) {
if ( preg_match( "/{$field->id}[^_]*{$index}_([0-9]+)/", $f_id, $m ) ) {
$matches[ $field->id ][] = intval( $m[1] );
}
}
$max_indexes[ $field->id ] = max( $matches[ $field->id ] );
}
return $max_indexes;
}
/**
* Recursively checks whether a multi-dimensional array is empty.
*
* @since 2.4
*
* @param $val
*
* @return bool
*/
public function empty_deep( $val ) {
$result = true;
if ( is_array( $val ) && count( $val ) > 0 ) {
foreach ( $val as $v ) {
$result = $result && $this->empty_deep( $v );
}
} else {
$result = empty( $val );
}
return $result;
}
/**
* Returns the sub-filters for the current field.
*
* @since 2.4
*
* @return array
*/
public function get_filter_sub_filters() {
$filters = array();
$fields = $this->fields;
foreach ( $fields as $field ) {
/** @var GF_Field $field */
$filter_settings = array(
'key' => $field->id,
'text' => GFFormsModel::get_label( $field, false, true ),
);
if ( is_array( $field->fields ) ) {
$filter_settings = $field->get_filter_settings();
$filters[] = $filter_settings;
continue;
}
$sub_filters = $field->get_filter_sub_filters();
if ( ! empty( $sub_filters ) ) {
$filter_settings['group'] = true;
$filter_settings['filters'] = $sub_filters;
} else {
$filter_settings['preventMultiple'] = false;
$filter_settings['operators'] = $field->get_filter_operators();
$values = $field->get_filter_values();
if ( ! empty( $values ) ) {
$filter_settings['values'] = $values;
}
}
$values = $field->get_filter_values();
if ( ! empty( $values ) ) {
$filter_settings['values'] = $values;
}
$filters[] = $filter_settings;
}
return $filters;
}
/**
* Returns the filter settings for the current field.
*
* If overriding to add custom settings call the parent method first to get the default settings.
*
* @since 2.4
*
* @return array
*/
public function get_filter_settings() {
$filter_settings = parent::get_filter_settings();
$filter_settings['isNestable'] = true;
return $filter_settings;
}
/**
* Format the entry value before it is used in entry exports and by framework add-ons using GFAddOn::get_field_value().
*
* @since 2.4
*
* @param array $entry The entry currently being processed.
* @param string $input_id The field or input ID.
* @param bool|false $use_text When processing choice based fields should the choice text be returned instead of the value.
* @param bool|false $is_csv Is the value going to be used in the .csv entries export?
*
* @return string|array
*/
public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) {
$export = $this->get_value_export_recursive( $entry, $input_id, $use_text, $is_csv );
return $export;
}
/**
* Format the entry value before it is used in entry exports and by framework add-ons using GFAddOn::get_field_value().
*
* @since 2.4
*
* @param array $entry The entry currently being processed.
* @param string $input_id The field or input ID.
* @param bool|false $use_text When processing choice based fields should the choice text be returned instead of the value.
* @param bool|false $is_csv Is the value going to be used in the .csv entries export?
*
* @return string|array
*/
public function get_value_export_recursive( $entry, $input_id = '', $use_text = false, $is_csv = false, $depth = 0, $padding = ' ' ) {
if ( empty( $input_id ) ) {
$input_id = $this->id;
}
$items = rgar( $entry, $input_id );
/* @var GF_Field[] $fields */
$fields = $this->fields;
$csv = array();
foreach ( $items as $item ) {
foreach ( $fields as $field ) {
$inputs = $field->get_entry_inputs();
if ( is_array( $inputs ) ) {
$field_value = array();
$field_keys = array_keys( $item );
foreach ( $field_keys as $input_id ) {
if ( is_numeric( $input_id ) && absint( $input_id ) == absint( $field->id ) ) {
$field_value[ $input_id ] = $item[ $input_id ];
}
}
} else {
$field_value = isset( $item[ $field->id ] ) ? $item[ $field->id ] : '';
$field_value = array( (string) $field->id => $field_value );
}
$label = str_repeat( $padding, $depth ) . GFFormsModel::get_label( $field );
if ( is_array( $field->fields ) ) {
$new_depth = $depth + 1;
$line = $label . "\n" . $field->get_value_export_recursive( $field_value, $field->id, $use_text, $is_csv, $new_depth, $padding );
if ( $depth == 0 ) {
$line .= "\n";
}
} else {
if ( 'list' === $field->get_input_type() && ! empty( $field_value[ $field->id ] ) ) {
$list_rows = maybe_unserialize( $field_value[ $field->id ] );
if ( is_array( $list_rows[0] ) ) {
$lines = array();
foreach ( $list_rows as $i => $list_row ) {
$row_label = $label . ' ' . ( $i + 1 );
// Prepare row value.
$row_value = implode( '|', $list_row );
if ( strpos( $row_value, '=' ) === 0 ) {
// Prevent Excel formulas
$row_value = "'" . $row_value;
}
$lines[] = $row_label . ': ' . $row_value;
}
$line = implode( "\n", $lines );
} else {
$value = implode( '|', $list_rows );
if ( strpos( $value, '=' ) === 0 ) {
// Prevent Excel formulas
$value = "'" . $value;
}
$line = $label . ': ' . $value;
}
} else {
$line = $label . ': ' . $field->get_value_export( $field_value, $field->id, $use_text, $is_csv );
}
}
$csv[] = $line;
}
}
return implode( "\n", $csv );
}
/**
* Store the modifiers so they can be accessed when preparing the {all_fields} and field merge tag output.
*
* @since 2.4
*
* @param array $modifiers An array of modifiers to be stored.
*/
public function set_modifiers( $modifiers ) {
parent::set_modifiers( $modifiers );
/* @var GF_Field $sub_field */
foreach ( $this->fields as $sub_field ) {
$sub_field->set_modifiers( $modifiers );
}
}
}
GF_Fields::register( new GF_Field_Repeater() );