plugin updates

This commit is contained in:
Tony Volpe
2024-11-20 22:40:39 -05:00
parent 0238f0c4ca
commit 3362947c6e
434 changed files with 13405 additions and 9202 deletions

View File

@@ -4,6 +4,8 @@ if ( ! class_exists( 'GFForms' ) ) {
die();
}
require_once( plugin_dir_path( __FILE__ ) . 'field-decorator-choice/class-gf-field-decorator-choice-checkbox-markup.php' );
class GF_Field_Checkbox extends GF_Field {
/**
@@ -120,6 +122,12 @@ class GF_Field_Checkbox extends GF_Field {
}
public function get_default_properties() {
return array(
'selectAllText' => esc_html__( 'Select All', 'gravityforms' ),
);
}
/**
* Returns the field inner markup.
*
@@ -135,6 +143,11 @@ class GF_Field_Checkbox extends GF_Field {
*/
public function get_field_input( $form, $value = '', $entry = null ) {
if ( $this->type == 'image_choice' ) {
$this->image_markup = new GF_Field_Decorator_Choice_Checkbox_Markup( $this );
return $this->image_markup->get_field_input( $form, $value, $entry );
}
$form_id = absint( $form['id'] );
if ( GFCommon::is_legacy_markup_enabled( $form ) ) {
@@ -151,14 +164,35 @@ class GF_Field_Checkbox extends GF_Field {
// Get checkbox choices markup.
$choices_markup = $this->get_checkbox_choices( $value, $disabled_text, $form_id );
if ( ! $this->enableSelectAll ) {
// Get button markup.
$button_markup = $this->get_button_markup( $value, $entry );
$select_all_enabled_class = $this->enableSelectAll ? 'gfield_choice--select_all_enabled' : '';
$limit_message = $this->get_limit_message();
if ( 'multi_choice' == $this->type || ! $this->enableSelectAll ) {
return sprintf(
"<div class='ginput_container ginput_container_checkbox'><div class='gfield_checkbox' id='%s'>%s</div></div>",
"<div class='ginput_container ginput_container_checkbox'>%s<div class='gfield_checkbox %s' id='%s'>%s</div></div>",
$limit_message,
$select_all_enabled_class,
esc_attr( $field_id ),
$choices_markup
);
}
return sprintf(
"<div class='ginput_container ginput_container_checkbox'>%s<div class='gfield_checkbox %s' id='%s'>%s%s</div></div>",
$limit_message,
$select_all_enabled_class,
esc_attr( $field_id ),
$choices_markup,
$button_markup
);
}
public function get_button_markup( $value, $entry ) {
/**
* Modify the "Select All" checkbox label.
*
@@ -192,16 +226,139 @@ class GF_Field_Checkbox extends GF_Field {
$deselect_label,
$all_selected ? 1 : 0,
$all_selected ? $deselect_label : $select_label,
$is_form_editor ? ' disabled="disabled"' : ''
$this->is_form_editor() ? ' disabled="disabled"' : ''
);
return sprintf(
"<div class='ginput_container ginput_container_checkbox'><div class='gfield_checkbox' id='%s'>%s%s</div></div>",
esc_attr( $field_id ),
$choices_markup,
$button_markup
);
return $button_markup;
}
/**
* Get the message that describes the choice limit.
*
* @since 2.9.0
*
* @return string
*/
public function get_limit_message() {
$text = $this->get_limit_message_text();
if ( ! $text ) {
return '';
}
$form_id = $this->formId;
$form = GFAPI::get_form( $form_id );
// If the validation message is the same as the limit message, and they both display above the field, don't display the limit message.
if ( $this->failed_validation && $this->validation_message === $text && $this->is_validation_above( $form ) ) {
return;
}
$id = $this->id;
return "<span class='gfield_choice_limit_message gfield_description' id='gfield_choice_limit_message_{$form_id}_{$id}'>{$text}</span>";
}
/**
* Get the text of the choice limit message, or return false if there is no limit.
*
* @since 2.9.0
*
* @return false|string
*/
public function get_limit_message_text() {
if ( $this->choiceLimit === 'exactly' && $this->choiceLimitNumber ) {
$message = sprintf(
esc_attr(
_n(
'Select exactly %s choice.',
'Select exactly %s choices.',
$this->choiceLimitNumber,
'gravityforms'
)
),
"<strong>$this->choiceLimitNumber</strong>"
);
/**
* Modify the message displayed when a checkbox is limited to an exact number of entries.
*
* @since 2.9.0
*
* @param string $message The message to filter.
* @param int $number The number of choices that must be selected.
* @param object $field The field currently being processed.
*/
return gf_apply_filters( array( 'gform_checkbox_limit_exact_message', $this->formId, $this->id ), $message, $this->choiceLimitNumber, $this );
}
if ( $this->choiceLimit === 'range' ) {
$min = $this->choiceLimitMin;
$max = $this->choiceLimitMax;
if ( ! $min && $max ) {
$message = sprintf(
esc_attr(
_n(
'Select up to %s choice.',
'Select up to %s choices.',
$max,
'gravityforms'
)
),
"<strong>$max</strong>"
);
/**
* Modify the message displayed when a checkbox is limited to a maximum number of choices.
*
* @since 2.9.0
*
* @param string $message The message to filter.
* @param int $max The maximum number of choices that must be selected.
* @param object $field The field currently being processed.
*/
return gf_apply_filters( array( 'gform_checkbox_limit_max_message', $this->formId, $this->id ), $message, $max, $this );
}
if ( ! $max && $min ) {
$message = sprintf(
esc_attr(
_n(
'Select at least %s choice.',
'Select at least %s choices.',
$min,
'gravityforms'
)
),
"<strong>$min</strong>"
);
/**
* Modify the message displayed when a checkbox is limited to a minimum number of choices.
*
* @since 2.9.0
*
* @param string $message The message to filter.
* @param int $min The minimum number of choices that must be selected.
* @param object $field The field currently being processed.
*/
return gf_apply_filters( array( 'gform_checkbox_limit_min_message', $this->formId, $this->id ), $message, $min, $this );
}
if( $min && $max ) {
$message = sprintf( esc_html__( 'Select between %s and %s choices.', 'gravityforms' ), "<strong>$min</strong>", "<strong>$max</strong>" );
/**
* Modify the message displayed when a checkbox is limited to a maximum number of entries.
*
* @since 2.9.0
*
* @param string $message The message to filter.
* @param int $min The minimum number of choices that must be selected.
* @param int $max The maximum number of choices that must be selected.
* @param object $field The field currently being processed.
*/
return gf_apply_filters( array( 'gform_checkbox_limit_range_message', $this->formId, $this->id ), $message, $min, $max, $this );
}
}
return false;
}
/**
@@ -259,7 +416,11 @@ class GF_Field_Checkbox extends GF_Field {
}
// Prepare input ID.
$input_id = $this->id . '.' . $choice_number;
if ( rgar( $choice, 'key' ) ) {
$input_id = $this->get_input_id_from_choice_key( $choice['key'] );
} else {
$input_id = $this->id . '.' . $choice_number;
}
if ( ( $this->is_form_editor() || ( ! isset( $_GET['gf_token'] ) && empty( $_POST ) ) ) && rgar( $choice, 'isSelected' ) ) {
$checkboxes_selected++;
@@ -273,7 +434,6 @@ class GF_Field_Checkbox extends GF_Field {
}
return $checkboxes_selected;
}
@@ -336,7 +496,17 @@ class GF_Field_Checkbox extends GF_Field {
$item = trim( $item );
if ( GFFormsModel::choice_value_match( $this, $this->choices[ $choice_index ], $item ) ) {
if ( rgar( $input, 'key' ) ) {
$choice_id = $this->get_choice_id_from_input_key( $input['key'] );
if ( '' == $choice_id ) {
continue;
}
$choice = $this->choices[ $choice_id ];
} else {
$choice = $this->choices[ $choice_index ];
}
if ( GFFormsModel::choice_value_match( $this, $choice, $item ) ) {
$value[ $input['id'] . '' ] = $item;
break;
}
@@ -356,7 +526,27 @@ class GF_Field_Checkbox extends GF_Field {
}
public function validate( $value, $form ) {
if ( $this->choiceLimit == 'exactly' ) {
$selected_choices_count = $this->get_selected_choices_count( $value );
if ( 0 === $selected_choices_count ) {
return;
}
if ( $selected_choices_count != $this->choiceLimitNumber ) {
$this->failed_validation = true;
$this->validation_message = $this->get_limit_message_text();
}
} elseif ( $this->choiceLimit == 'range' ) {
$selected_choices_count = $this->get_selected_choices_count( $value );
if ( 0 === $selected_choices_count ) {
return;
}
if ( ( $this->choiceLimitMin && $selected_choices_count < $this->choiceLimitMin ) || ( $this->choiceLimitMax && $selected_choices_count > $this->choiceLimitMax ) ) {
$this->failed_validation = true;
$this->validation_message = $this->get_limit_message_text();
}
}
}
@@ -403,7 +593,6 @@ class GF_Field_Checkbox extends GF_Field {
if ( $this->type == 'post_category' ) {
$value = GFCommon::prepare_post_category_value( $value, $this, 'entry_list' );
}
} else {
$value = '';
@@ -437,7 +626,6 @@ class GF_Field_Checkbox extends GF_Field {
* @return string
*/
public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
if ( is_array( $value ) ) {
$items = '';
@@ -511,6 +699,7 @@ class GF_Field_Checkbox extends GF_Field {
$use_value = in_array( 'value', $modifiers );
$format_currency = in_array( 'currency', $modifiers );
$use_price = $format_currency || in_array( 'price', $modifiers );
$image_url = in_array( 'img_url', $modifiers );
if ( is_array( $raw_value ) && (string) intval( $input_id ) != $input_id ) {
$items = array( $input_id => $value ); // Float input IDs. (i.e. 4.1 ). Used when targeting specific checkbox items.
@@ -524,38 +713,40 @@ class GF_Field_Checkbox extends GF_Field {
// Get the items available within the merge tags.
foreach ( $items as $input_id => $item ) {
switch (true) {
// If the 'value' modifier was passed.
case $use_value:
list( $val, $price ) = rgexplode( '|', $item, 2 );
break;
// If the 'value' modifier was passed.
if ( $use_value ) {
// If the 'price' or 'currency' modifiers were passed.
case $use_price:
list( $name, $val ) = rgexplode( '|', $item, 2 );
if ( $format_currency ) {
$val = GFCommon::to_money( $val, rgar( $entry, 'currency' ) );
}
break;
list( $val, $price ) = rgexplode( '|', $item, 2 );
// If the 'image_url' modifier was passed.
case $image_url:
$image_choice = new GF_Field_Image_Choice( $this );
$val = $image_choice->get_merge_tag_img_url( $raw_value, $input_id, $entry, $form, $this );
break;
// If the 'price' or 'currency' modifiers were passed.
} elseif ( $use_price ) {
list( $name, $val ) = rgexplode( '|', $item, 2 );
if ( $format_currency ) {
$val = GFCommon::to_money( $val, rgar( $entry, 'currency' ) );
}
// If this is a post category checkbox.
} else if ( $this->type == 'post_category' ) {
$use_id = strtolower( $modifier ) == 'id';
$item_value = GFCommon::format_post_category( $item, $use_id );
$val = GFFormsModel::is_field_hidden( $form, $this, array(), $entry ) ? '' : $item_value;
// If no modifiers were passed.
} else {
$val = GFFormsModel::is_field_hidden( $form, $this, array(), $entry ) ? '' : RGFormsModel::get_choice_text( $this, $raw_value, $input_id );
// If this is a post category checkbox.
case $this->type == 'post_category':
$use_id = strtolower( $modifier ) == 'id';
$item_value = GFCommon::format_post_category( $item, $use_id );
$val = GFFormsModel::is_field_hidden( $form, $this, array(), $entry ) ? '' : $item_value;
break;
// If no modifiers were passed.
default:
$val = GFFormsModel::is_field_hidden( $form, $this, array(), $entry ) ? '' : RGFormsModel::get_choice_text( $this, $raw_value, $input_id );
break;
}
$ary[] = GFCommon::format_variable_value( $val, $url_encode, $esc_html, $format );
}
return GFCommon::implode_non_blank( ', ', $ary );
@@ -606,6 +797,29 @@ class GF_Field_Checkbox extends GF_Field {
}
/**
* Return the entry inputs in the order they are configured in the form editor.
*
* @since 2.9
*
* @return array|null
*/
public function get_entry_inputs() {
if ( $this->has_persistent_choices() && is_array( $this->inputs ) ) {
$inputs_by_key = array_column( $this->inputs, null, 'key' );
$sorted_inputs = array();
foreach ( $this->choices as $choice ) {
if ( isset( $inputs_by_key[ $choice['key'] ] ) ) {
$sorted_inputs[] = $inputs_by_key[ $choice['key'] ];
}
}
return $sorted_inputs;
} else {
return $this->inputs;
}
}
/**
* Format the entry value before it is used in entry exports and by framework add-ons using GFAddOn::get_field_value().
*
@@ -690,11 +904,23 @@ class GF_Field_Checkbox extends GF_Field {
$choice_number = 1;
$count = 1;
/**
* A filter that allows for the setting of the maximum number of choices shown in
* the form editor for choice based fields (radio, checkbox, image, and multiple choice).
*
* @since 2.9
*
* @param int $max_choices_visible_count The default number of choices visible is 5.
* @param object $field The current field object.
*/
$max_choices_count = gf_apply_filters( array( 'gform_field_choices_max_count_visible', $form_id ), 5, $this );
$legacy_markup = GFCommon::is_legacy_markup_enabled( $form_id );
$tag = $legacy_markup ? 'li' : 'div';
// Add Select All choice.
// Add Select All choice for legacy markup.
if ( $this->enableSelectAll && $legacy_markup ) {
/**
@@ -754,11 +980,22 @@ class GF_Field_Checkbox extends GF_Field {
}
$need_aria_describedby = true;
// Add select all choice for the Multiple Choice field.
if ( $this->type == 'multi_choice' && $this->enableSelectAll ) {
$this->choice_field = new GF_Field_Multiple_Choice( $this );
$selected_choices_count = $this->get_selected_choices_count( $value );
$tabindex = $this->get_tabindex();
$need_aria_describedby = false; // We're going to add the aria-describedby attribute to the "Select All" choice, so we don't need it later on the first choice.
$choices .= $this->choice_field->get_choice_field_select_all_markup( $value, $tabindex, $selected_choices_count );
}
// Loop through field choices.
foreach ( $this->choices as $choice ) {
// Get aria-describedby if this is the first choice
$aria_describedby = $choice_number === 1 ? $this->get_aria_describedby() : '';
$aria_describedby = ( $choice_number === 1 && $need_aria_describedby ) ? $this->get_choice_aria_describedby( $form_id ) : '';
// Hack to skip numbers ending in 0, so that 5.1 doesn't conflict with 5.10.
if ( $choice_number % 10 == 0 ) {
@@ -766,7 +1003,13 @@ class GF_Field_Checkbox extends GF_Field {
}
// Prepare input ID.
$input_id = $this->id . '.' . $choice_number;
$input_id = '';
if ( $this->has_persistent_choices() ) {
$input_id = $this->get_input_id_from_choice_key( $choice['key'] );
} else {
// Regular checkboxes field generates input IDs on the fly.
$input_id = $this->id . '.' . $choice_number;
}
if ( $is_entry_detail || $is_form_editor || $form_id == 0 ) {
$id = $this->id . '_' . $choice_number ++;
@@ -774,16 +1017,7 @@ class GF_Field_Checkbox extends GF_Field {
$id = $form_id . '_' . $this->id . '_' . $choice_number ++;
}
if ( ( $is_form_editor || ( ! isset( $_GET['gf_token'] ) && empty( $_POST ) ) ) && rgar( $choice, 'isSelected' ) ) {
$checked = "checked='checked'";
} elseif ( is_array( $value ) && GFFormsModel::choice_value_match( $this, $choice, rgget( $input_id, $value ) ) ) {
$checked = "checked='checked'";
} elseif ( ! is_array( $value ) && GFFormsModel::choice_value_match( $this, $choice, $value ) && ! empty( $_POST[ 'is_submit_' . $form_id ] ) ) {
$checked = "checked='checked'";
} else {
$checked = '';
}
$checked = $this->get_checked_attribute( $choice, $value, $input_id, $form_id );
$tabindex = $this->get_tabindex();
$choice_value = $choice['value'];
@@ -812,7 +1046,7 @@ class GF_Field_Checkbox extends GF_Field {
$is_admin = $is_entry_detail || $is_form_editor;
if ( $is_admin && rgget('view') != 'entry' && $count >= 5 ) {
if ( $is_admin && rgget('view') != 'entry' && $count >= $max_choices_count ) {
break;
}
@@ -823,7 +1057,7 @@ class GF_Field_Checkbox extends GF_Field {
$total = sizeof( $this->choices );
if ( $count < $total ) {
$choices .= "<{$tag} class='gchoice_total'>" . sprintf( esc_html__( '%d of %d items shown. Edit field to view all', 'gravityforms' ), $count, $total ) . "</{$tag}>";
$choices .= "<{$tag} class='gchoice_total'><span>" . sprintf( esc_html__( '%d of %d items shown. Edit choices to view all.', 'gravityforms' ), $count, $total ) . "</span></{$tag}>";
}
}
@@ -834,12 +1068,53 @@ class GF_Field_Checkbox extends GF_Field {
* @since Unknown
*
* @param string $choices The string containing the choices to be filtered.
* @param object $field Ahe field currently being processed.
* @param object $field The field currently being processed.
*/
return gf_apply_filters( array( 'gform_field_choices', $this->formId, $this->id ), $choices, $this );
}
public function get_checked_attribute( $choice, $value, $input_id, $form_id ) {
$is_form_editor = $this->is_form_editor();
if ( ( $is_form_editor || ( ! isset( $_GET['gf_token'] ) && empty( $_POST ) ) ) && rgar( $choice, 'isSelected' ) ) {
$checked = "checked='checked'";
} elseif ( is_array( $value ) && GFFormsModel::choice_value_match( $this, $choice, rgget( $input_id, $value ) ) ) {
$checked = "checked='checked'";
} elseif ( ! is_array( $value ) && GFFormsModel::choice_value_match( $this, $choice, $value ) && ! empty( $_POST[ 'is_submit_' . $form_id ] ) ) {
$checked = "checked='checked'";
} else {
$checked = '';
}
return $checked;
}
/**
* Get the aria-describedby attribute for the first choice.
*
* @since 2.9.0
* @access public
*
* @param int $form_id The current form ID.
* @param array $describedby Additional describedby attribute value.
*
* @return string
*/
public function get_choice_aria_describedby( $form_id, $describedby = array() ) {
$limit_describedby = array();
if ( is_array( $describedby ) ) {
$limit_describedby = array_merge( $limit_describedby, $describedby );
}
if ( $this->get_limit_message_text() ) {
$limit_describedby[] = 'gfield_choice_limit_message_' . $form_id . '_' . $this->id;
}
return $this->get_aria_describedby( $limit_describedby );
}
/**
* Determine if a specific checkbox is checked.
*