get_legacy_field_input( $form, $value, $entry ); } $form_id = $form['id']; $is_form_editor = $this->is_form_editor(); if ( ! empty( $value ) ) { $value = maybe_unserialize( $value ); } if ( is_array( $value ) ) { if ( ! is_array( $value[0] ) ) { $value = $this->create_list_array( $value ); } } else { $value = array( array() ); } $has_columns = is_array( $this->choices ); $columns = $has_columns ? $this->choices : array( array() ); $list = ''; $list .= "
" . "
"; if ( $has_columns ) { $list .= '
'; foreach ( $columns as $column ) { // a11y: scope="col" $list .= '
' . esc_html( $column['text'] ) . '
'; } if ( $this->maxRows != 1 ) { // Using td instead of th because empty th tags break a11y. $list .= '
 
'; } $list .= '
'; } $delete_display = count( $value ) == 1 ? 'style="visibility:hidden;"' : ''; $maxRow = intval( $this->maxRows ); $disabled_icon_class = ! empty( $maxRow ) && count( $value ) >= $maxRow ? 'gfield_icon_disabled' : ''; $add_icon = ! empty( $this->addIconUrl ) ? $this->addIconUrl : GFCommon::get_base_url() . '/images/list-add.svg'; $delete_icon = ! empty( $this->deleteIconUrl ) ? $this->deleteIconUrl : GFCommon::get_base_url() . '/images/list-remove.svg'; $add_events = $is_form_editor ? '' : "onclick='gformAddListItem(this, {$maxRow})'"; $delete_events = $is_form_editor ? '' : "onclick='gformDeleteListItem(this, {$maxRow})'"; $list .= '
'; $rownum = 1; foreach ( $value as $item ) { $odd_even = ( $rownum % 2 ) == 0 ? 'even' : 'odd'; $list .= "
"; $colnum = 1; foreach ( $columns as $column ) { $data_label = ''; // Getting value. Taking into account columns being added/removed from form meta. if ( is_array( $item ) ) { if ( $has_columns ) { $val = rgar( $item, $column['text'] ); $data_label = "data-label='" . esc_attr( $column['text'] ) . "'"; } else { $vals = array_values( $item ); $val = rgar( $vals, 0 ); } } else { $val = $colnum == 1 ? $item : ''; } $list .= "
" . $this->get_list_input( $has_columns, $column, $val, $form_id, $rownum ) . '
'; $colnum ++; } if ( $this->maxRows != 1 ) { $aria_label_template = __( 'Remove row {0}', 'gravityforms' ); $disabled = $is_form_editor ? 'disabled=\'disabled\'' : ''; $list .= "
"; $list .= " " . " "; $list .= '
'; } $list .= '
'; if ( ! empty( $maxRow ) && $rownum >= $maxRow ) { break; } $rownum ++; } $list .= '
'; $list .= '
'; return $list; } /** * Builds the field input HTML markup. * * @since Unknown * @access public * * @param array $form The Form Object. * @param string $value The field value. Defaults to empty string. * @param null|array $entry The Entry Object. Defaults to null. * * @return string The List field HTML markup. */ public function get_legacy_field_input( $form, $value = '', $entry = null ) { $form_id = $form['id']; $is_form_editor = $this->is_form_editor(); if ( ! empty( $value ) ) { $value = maybe_unserialize( $value ); } if ( is_array( $value ) ) { if ( ! is_array( $value[0] ) ) { $value = $this->create_list_array( $value ); } } else { $value = array( array() ); } $has_columns = is_array( $this->choices ); $columns = $has_columns ? $this->choices : array( array() ); $list = ''; if ( ! self::$_style_block_printed ){ // This style block needs to be inline so that the list field continues to work even if the option to turn off CSS output is activated. $list .= ''; self::$_style_block_printed = true; } $list .= "
" . ""; if ( $has_columns ) { $list .= ''; for ( $colnum = 1; $colnum <= count( $columns ) + 1; $colnum++ ) { $odd_even = ( $colnum % 2 ) == 0 ? 'even' : 'odd'; $list .= sprintf( "", $this->id, $colnum, $odd_even ); } $list .= ''; $list .= ''; foreach ( $columns as $column ) { // a11y: scope="col" $list .= ''; } if ( $this->maxRows != 1 ) { // Using td instead of th because empty th tags break a11y. $list .= ''; } $list .= ''; } else { $list .= '' . "" . "" . ''; } $delete_display = count( $value ) == 1 ? 'style="visibility:hidden;"' : ''; $maxRow = intval( $this->maxRows ); $disabled_icon_class = ! empty( $maxRow ) && count( $value ) >= $maxRow ? 'gfield_icon_disabled' : ''; $add_icon = ! empty( $this->addIconUrl ) ? $this->addIconUrl : GFCommon::get_base_url() . '/images/list-add.svg'; $delete_icon = ! empty( $this->deleteIconUrl ) ? $this->deleteIconUrl : GFCommon::get_base_url() . '/images/list-remove.svg'; $add_events = $is_form_editor ? '' : "onclick='gformAddListItem(this, {$maxRow})' onkeypress='gformAddListItem(this, {$maxRow})'"; $delete_events = $is_form_editor ? '' : "onclick='gformDeleteListItem(this, {$maxRow})' onkeypress='gformDeleteListItem(this, {$maxRow})'"; $list .= ''; $rownum = 1; foreach ( $value as $item ) { $odd_even = ( $rownum % 2 ) == 0 ? 'even' : 'odd'; $list .= ""; $colnum = 1; foreach ( $columns as $column ) { $data_label = ''; // Getting value. Taking into account columns being added/removed from form meta. if ( is_array( $item ) ) { if ( $has_columns ) { $val = rgar( $item, $column['text'] ); $data_label = "data-label='" . esc_attr( $column['text'] ) . "'"; } else { $vals = array_values( $item ); $val = rgar( $vals, 0 ); } } else { $val = $colnum == 1 ? $item : ''; } $list .= "'; $colnum ++; } if ( $this->maxRows != 1 ) { // Can't replace these icons with the webfont versions since they appear on the front end. $list .= "'; } $list .= ''; if ( ! empty( $maxRow ) && $rownum >= $maxRow ) { break; } $rownum ++; } $list .= ''; $list .= '
' . esc_html( $column['text'] ) . ' 
" . $this->get_list_input( $has_columns, $column, $val, $form_id, null ) . '"; $list .= " " . " "; $list .= '
'; return $list; } /** * Builds the input that will be inside the List field. * * @since Unknown * @access public * * @param bool $has_columns If the input has columns. * @param array $column The column details. * @param string $value The existing value of the input. * @param int $form_id The form ID. * @param int $row The row number to which the input belongs. * * @return string The input HTML markup. */ public function get_list_input( $has_columns, $column, $value, $form_id, $row ) { $tabindex = $this->get_tabindex(); $disabled = $this->is_form_editor() ? 'disabled' : ''; $required_attribute = $this->isRequired ? 'aria-required="true"' : ''; $invalid_attribute = $this->failed_validation ? "aria-invalid='true'" : "aria-invalid='false'"; $aria_describedby = $this->get_aria_describedby(); $column_index = 1; if ( $has_columns && is_array( $this->choices ) ) { foreach ( $this->choices as $choice ) { if ( $choice['text'] == $column['text'] ) { break; } $column_index ++; } } $input_info = array( 'type' => 'text' ); $column_text = rgar( $column, 'text' ); $aria_label_template = isset( $column['text'] ) ? $column_text : $this->label; $aria_label_template .= ", Row {0}"; /** * Filters the column input. * * @since Unknown * * @param array $input_info Information about the input. Contains the input type. * @param object GF_Field_List Field object for this field type. * @param string $column['text'] The column text value. * @param int $form_id The form ID. */ $input_info = gf_apply_filters( array( 'gform_column_input', $form_id, $this->id, $column_index ), $input_info, $this, $column_text, $value, $form_id ); switch ( $input_info['type'] ) { case 'select' : $input = "'; break; default : // a11y: inputs without a label must have the aria-label attribute set. $input = ""; break; } /** * Filters the column input HTML markup. * * @since Unknown * * @param string $input The input markup. * @param array $input_info The information that was used to build the input. * @param object GF_Field_List An instance of the List field object. * @param string $column['text'] The column text value. * @param int $form_id The form ID. */ return gf_apply_filters( array( 'gform_column_input_content', $form_id, $this->id, $column_index ), $input, $input_info, $this, rgar( $column, 'text' ), $value, $form_id ); } /** * Get field label class. * * @since unknown * @since 2.5 Added `screen-reader-text` if the label hasn't been set; added `gfield_label_before_complex` if it has choices. * * @return string */ public function get_field_label_class() { $class = 'gfield_label'; // Added `screen-reader-text` if the label hasn't been set. $class .= ( rgblank( $this->label ) ) ? ' screen-reader-text' : ''; // Added `gfield_label_before_complex` if it has choices. $class .= is_array( $this->choices ) ? ' gfield_label_before_complex' : ''; return $class; } /** * Whether this field expects an array during submission. * * @since 2.4 * * @return bool */ public function is_value_submission_array() { return true; } /** * Gets the value of te field from the form submission. * * @since Unknown * @access public * * @param array $field_values The properties to search for. * @param bool $get_from_post_global_var If the global GET variable should be used to obtain the value. Defaults to true. * * @return array The submission value. */ public function get_value_submission( $field_values, $get_from_post_global_var = true ) { $value = $this->get_input_value_submission( 'input_' . $this->id, $this->inputName, $field_values, $get_from_post_global_var ); return $value; } /** * Creates an array from the list items. Recurses if the field is inside a Repeater. * * @since 2.4 * * @param $value * * @return array */ public function create_list_array_recursive( $value ) { if ( isset( $value[0] ) && is_array( $value[0] ) ) { $new_value = array(); foreach ( $value as $k => $v ) { $new_value[ $k ] = $this->create_list_array_recursive( $v ); } } else { $new_value = $this->create_list_array( $value ); } return $new_value; } /** * Check if the submission value is empty. * * @since Unknown * @access public * * @param int $form_id The form ID to check. * * @return bool True if empty. False otherwise. */ public function is_value_submission_empty( $form_id ) { $value = rgpost( 'input_' . $this->id ); if ( is_array( $value ) ) { // Empty if all inputs are empty (for inputs with the same name). foreach ( $value as $input ) { if ( strlen( trim( $input ) ) > 0 ) { return false; } } } return true; } /** * Gets the field value HTML markup to be used on the entry detail page. * * @since Unknown * @access public * * @param array $value The submitted entry value. * @param string $currency Not used. * @param bool $use_text Not used. * @param string $format The format to be used when building the items. * Accepted values are text, url, or html. Defaults to html. * @param string $media Defines how the content will be output. * Accepted values are screen or email. Defaults to screen. * * @return string The HTML markup to be displayed. */ public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) { if ( empty( $value ) ) { return ''; } $value = maybe_unserialize( $value ); if( ! is_array( $value ) || ! isset( $value[0] ) ) { return ''; } $has_columns = is_array( $value[0] ); if ( ! $has_columns ) { $items = ''; foreach ( $value as $key => $item ) { if ( ! empty( $item ) ) { $item = wp_kses_post( $item ); switch ( $format ) { case 'text' : $items .= $item . ', '; break; case 'url' : $items .= $item . ','; break; default : if ( $media == 'email' ) { $items .= "
  • {$item}
  • "; } else { $items .= "
  • {$item}
  • "; } break; } } } if ( empty( $items ) ) { return ''; } elseif ( $format == 'text' ) { return substr( $items, 0, strlen( $items ) - 2 ); // Removing last comma. } elseif ( $format == 'url' ) { return substr( $items, 0, strlen( $items ) - 1 ); // Removing last comma. } elseif ( $media == 'email' ) { return ""; } else { return ""; } } elseif ( is_array( $value ) ) { $columns = array_keys( $value[0] ); $list = ''; switch ( $format ) { case 'text' : $is_first_row = true; foreach ( $value as $item ) { if ( ! $is_first_row ) { $list .= "\n\n" . $this->label . ': '; } $item = array_map( 'wp_kses_post', $item ); $list .= implode( ',', array_values( $item ) ); $is_first_row = false; } break; case 'url' : foreach ( $value as $item ) { $item = array_map( 'wp_kses_post', $item ); $list .= implode( "|", array_values( $item ) ) . ','; } if ( ! empty( $list ) ) { $list = substr( $list, 0, strlen( $list ) - 1 ); } break; default : if ( $media == 'email' ) { $list = "\n"; //reading columns from entry data foreach ( $columns as $column ) { $list .= "' . "\n"; } $list .= '' . "\n"; $list .= ""; foreach ( $value as $item ) { $list .= ''; foreach ( $columns as $column ) { $val = rgar( $item, $column ); $val = wp_kses_post( $val ); $list .= "\n"; } $list .= '' . "\n"; } $list .= '
    " . esc_html( $column ) . '
    {$val}
    ' . "\n"; } else { $list = ""; // Reading columns from entry data. foreach ( $columns as $column ) { $list .= '' . "\n"; } $list .= '' . "\n"; $list .= ''; foreach ( $value as $item ) { $list .= ''; foreach ( $columns as $column ) { $val = rgar( $item, $column ); $val = wp_kses_post( $val ); $list .= "\n"; } $list .= '' . "\n"; } $list .= '
    ' . esc_html( $column ) . '
    {$val}
    ' . "\n"; } break; } return $list; } return ''; } /** * Gets the value of the field when the entry is saved. * * @since Unknown * @access public * * @param string $value The value to use. * @param array $form The form that the entry is associated with. * @param string $input_name The name of the input containing the value. * @param int $lead_id The entry ID. * @param array $lead The Entry Object. * * @return string The entry value. Escaped. */ public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) { if ( $this->is_administrative() && $this->allowsPrepopulate ) { $value = json_decode( $value ); } if ( GFCommon::is_empty_array( $value ) ) { $value = ''; } else { $value = $this->create_list_array( $value ); $value = serialize( $value ); } $value_safe = $this->sanitize_entry_value( $value, $form['id'] ); return $value_safe; } /** * Gets merge tag values. * * @since Unknown * @access public * * @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 ) { $modifiers = $this->get_modifiers(); $allowed_modifiers = array( 'text', 'html', 'url' ); if( $found_modifiers = array_intersect( $modifiers, $allowed_modifiers ) ) { $output_format = $found_modifiers[0]; } else { $output_format = $format; } return GFCommon::get_lead_field_display( $this, $raw_value, $entry['currency'], true, $output_format ); } /** * Format the entry value for display on the entries list page. * * By default, the List field will not be available for selection on the entry list. * Use the gform_display_field_select_columns_entry_list filter to make the list field available. * * * @since 2.4 * * @param string|array $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 ) { return GFCommon::get_lead_field_display( $this, $value, $entry['currency'], true, 'html' ); } /** * Creates an array from the list items. * * @since Unknown * @access public * * @param array $value The pre-formatted list. * * @return array The list rows. */ function create_list_array( $value ) { if ( ! $this->enableColumns ) { return $value; } else { $value = empty( $value ) ? array() : $value; $col_count = count( $this->choices ); $rows = array(); $row_count = count( $value ) / $col_count; $col_index = 0; for ( $i = 0; $i < $row_count; $i ++ ) { $row = array(); foreach ( $this->choices as $column ) { $row[ $column['text'] ] = rgar( $value, $col_index ); $col_index ++; } $rows[] = $row; } return $rows; } } /** * Sanitizes the field settings. * * @since Unknown * @access public */ public function sanitize_settings() { parent::sanitize_settings(); $this->maxRows = absint( $this->maxRows ); $this->enableColumns = (bool) $this->enableColumns; } /** * Gets the field value, formatted for exports. For CSV export return an array. * * @since Unknown * @access public * * @used-by GFExport::start_export() * @used-by GFAddOn::get_field_value() * @uses GF_Field_List::$id * @uses GF_Field_List::$enableColumns * @uses GF_Field_List::$choices * @uses GFCommon::implode_non_blank() * * @param array $entry The Entry Object. * @param string $input_id Input ID to export. If not defined, uses the current input ID. Defaults to empty string. * @param bool $use_text Not used. Defaults to false. * @param bool $is_csv Is the value going to be used in the CSV export? Defaults to false. * * @return string|array */ public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) { if ( empty( $input_id ) ) { $input_id = $this->id; } elseif ( ! ctype_digit( $input_id ) ) { $field_id_array = explode( '.', $input_id ); $input_id = rgar( $field_id_array, 0 ); $column_num = rgar( $field_id_array, 1 ); } $value = rgar( $entry, $input_id ); $value = maybe_unserialize( $value ); if ( empty( $value ) || $is_csv ) { return $value; } $list_values = $column_values = $value; if ( isset( $column_num ) && is_numeric( $column_num ) && $this->enableColumns ) { $column = rgars( $this->choices, "{$column_num}/text" ); $column_values = array(); foreach ( $list_values as $value ) { $column_values[] = rgar( $value, $column ); } } elseif ( $this->enableColumns ) { return json_encode( $list_values ); } return GFCommon::implode_non_blank( ', ', $column_values ); } // # FIELD FILTER UI HELPERS --------------------------------------------------------------------------------------- /** * Returns the filter operators for the current field. * * @since 2.4 * * @return array */ public function get_filter_operators() { return array( 'contains' ); } } // Register the list field. GF_Fields::register( new GF_Field_List() );