'', 'prefix' => '', 'translation_strings' => array(), ); $args = wp_parse_args( $args, $defaults ); foreach ( $args as $name => $value ) { $this->$name = $value; } } /** * Get field description for display. * * @param array $args settings Arguments array. * * @return string Description of the field. */ public function get_field_description( $args ) { $desc = ! empty( $args['desc'] ) ? '

' . wp_kses_post( $args['desc'] ) . '

' : ''; /** * After Settings Output filter * * @param string $desc Description of the field. * @param array $args Arguments array. */ $desc = apply_filters( $this->prefix . '_setting_field_description', $desc, $args ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound return $desc; } /** * Get the value of a settings field. * * @param string $option Settings field name. * @param mixed $default_value Default value if option is not found. * @return mixed */ public function get_option( $option, $default_value = '' ) { $options = \get_option( $this->settings_key ); if ( isset( $options[ $option ] ) ) { return $options[ $option ]; } return $default_value; } /** * Get field value from args or options. * * @param array $args Field arguments. * @return mixed Field value. */ protected function get_field_value( $args ) { return $args['value'] ?? $this->get_option( $args['id'], $args['default'] ?? '' ); } /** * Get sanitized field class string. * * @param array $args Field arguments. * @param string $default_class Default class to prepend. * @return string Sanitized class string. */ protected function get_field_class( $args, $default_class = '' ) { $class = implode( ' ', array_map( 'sanitize_html_class', explode( ' ', $args['field_class'] ?? '' ) ) ); if ( $default_class ) { $class = $default_class . ' ' . $class; } return trim( $class ); } /** * Get placeholder attribute string. * * @param array $args Field arguments. * @return string Placeholder attribute or empty string. */ protected function get_placeholder_attribute( $args ) { return empty( $args['placeholder'] ) ? '' : ' placeholder="' . esc_attr( $args['placeholder'] ) . '"'; } /** * Get boolean state attributes (disabled, readonly, required). * * @param array $args Field arguments. * @return string Concatenated boolean attributes. */ protected function get_boolean_attributes( $args ) { $disabled = ( ! empty( $args['disabled'] ) || ! empty( $args['pro'] ) ) ? ' disabled="disabled"' : ''; $readonly = ( isset( $args['readonly'] ) && true === $args['readonly'] ) ? ' readonly="readonly"' : ''; $required = ( isset( $args['required'] ) && true === $args['required'] ) ? ' required' : ''; return $disabled . $readonly . $required; } /** * Get field ID and name attributes. * * @param array $args Field arguments. * @return array Array containing field_id and field_name. */ protected function get_field_attributes( $args ) { $id = sanitize_key( $args['id'] ); if ( isset( $args['_repeater_id'] ) && isset( $args['_index'] ) ) { $field_id = sprintf( '%s-%s-%s-fields-%s', $this->settings_key, $args['_repeater_id'], $args['_index'], $id ); $field_name = sprintf( '%s[%s][%s][fields][%s]', $this->settings_key, $args['_repeater_id'], $args['_index'], $id ); } else { $field_id = $this->settings_key . '-' . $id; $field_name = $this->settings_key . '[' . $id . ']'; } return array( 'field_id' => $field_id, 'field_name' => $field_name, ); } /** * Returns the allowed HTML tags and attributes for settings form output. * * Use the `{prefix}_settings_form_allowed_html` filter to add extra tags or * attributes needed by custom field types or `field_attributes` entries. * * @return array> */ protected function get_allowed_html(): array { $allowed = wp_kses_allowed_html( 'post' ); $form_tags = array( 'input' => array( 'type' => true, 'name' => true, 'id' => true, 'value' => true, 'class' => true, 'style' => true, 'checked' => true, 'disabled' => true, 'readonly' => true, 'required' => true, 'placeholder' => true, 'size' => true, 'min' => true, 'max' => true, 'step' => true, 'multiple' => true, 'autocomplete' => true, // Tom Select data attributes (built-in Settings API feature). 'data-wp-prefix' => true, 'data-wp-action' => true, 'data-wp-nonce' => true, 'data-wp-endpoint' => true, 'data-ts-config' => true, 'data-wp-extra-fields' => true, ), 'select' => array( 'id' => true, 'name' => true, 'class' => true, 'style' => true, 'disabled' => true, 'multiple' => true, 'size' => true, 'autocomplete' => true, // Tom Select data attributes (built-in Settings API feature). 'data-wp-prefix' => true, 'data-wp-action' => true, 'data-wp-nonce' => true, 'data-wp-endpoint' => true, 'data-ts-config' => true, 'data-wp-extra-fields' => true, ), 'option' => array( 'value' => true, 'selected' => true, 'disabled' => true, ), 'optgroup' => array( 'label' => true, 'disabled' => true, ), 'textarea' => array( 'id' => true, 'name' => true, 'class' => true, 'style' => true, 'rows' => true, 'cols' => true, 'disabled' => true, 'readonly' => true, 'required' => true, 'placeholder' => true, 'autocomplete' => true, ), 'label' => array( 'for' => true, 'class' => true, 'style' => true, ), 'button' => array( 'type' => true, 'id' => true, 'class' => true, 'style' => true, 'disabled' => true, ), 'template' => array( 'class' => true, 'data-id' => true, ), ); $allowed = array_replace_recursive( $allowed, $form_tags ); /** * Filters the allowed HTML tags and attributes for settings form output. * * Use this filter to add data attributes or custom elements required by * field_attributes entries in your registered settings. * * @param array> $allowed Allowed HTML tags/attributes. */ return apply_filters( $this->prefix . '_settings_form_allowed_html', $allowed ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Miscellaneous callback funcion * * @param array $args Arguments array. * @return void */ public function callback_missing( $args ) { /* translators: 1: Code. */ printf( 'The callback function used for the %1$s setting is missing.', '' . esc_attr( $args['id'] ) . '' ); } /** * Header Callback * * Renders the header. * * @param array $args Arguments passed by the setting. * @return void */ public function callback_header( $args ) { $html = $this->get_field_description( $args ); /** * After Settings Output filter * * @param string $html HTML string. * @param array $args Arguments array. */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Descriptive text callback. * * Renders descriptive text onto the settings field. * * @param array $args Array of arguments. * @return void */ public function callback_descriptive_text( $args ) { $this->callback_header( $args ); } /** * Build additional attributes string from field_attributes. * * @param array $args Field arguments. * @return string Additional attributes string. */ protected function build_field_attributes( $args ) { $attributes = ''; foreach ( (array) ( $args['field_attributes'] ?? array() ) as $attribute => $val ) { $attr_name = sanitize_key( $attribute ); if ( empty( $attr_name ) ) { continue; } if ( true === $val ) { $attributes .= ' ' . $attr_name; } elseif ( false !== $val ) { $attributes .= sprintf( ' %1$s="%2$s"', $attr_name, esc_attr( $val ) ); } } return $attributes; } /** * Display text fields. * * @param array $args Array of arguments. */ public function callback_text( $args ) { $value = $this->get_field_value( $args ); $size = sanitize_html_class( $args['size'] ?? 'regular' ); $class = $this->get_field_class( $args ); $placeholder = $this->get_placeholder_attribute( $args ); $attributes = $this->get_boolean_attributes( $args ) . $this->build_field_attributes( $args ); $field_attributes = $this->get_field_attributes( $args ); $html = sprintf( '', $field_attributes['field_id'], $field_attributes['field_name'], $class . ' ' . $size . '-text', esc_attr( stripslashes( $value ) ), $attributes, $placeholder ); $html .= $this->get_field_description( $args ); /** * This filter has been defined in class-settings-api.php */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Display url fields. * * @param array $args Array of arguments. */ public function callback_url( $args ) { $this->callback_text( $args ); } /** * Display csv fields. * * @param array $args Array of arguments. */ public function callback_csv( $args ) { $this->callback_text( $args ); } /** * Display color fields. * * @param array $args Array of arguments. */ public function callback_color( $args ) { // Add color-field class for wpColorPicker initialization. $args['field_class'] = ! empty( $args['field_class'] ) ? $args['field_class'] . ' color-field' : 'color-field'; $this->callback_text( $args ); } /** * Display numbercsv fields. * * @param array $args Array of arguments. */ public function callback_numbercsv( $args ) { $this->callback_text( $args ); } /** * Display postids fields. * * @param array $args Array of arguments. */ public function callback_postids( $args ) { $this->callback_text( $args ); } /** * Display textarea. * * @param array $args Array of arguments. * @return void */ public function callback_textarea( $args ) { $value = $this->get_field_value( $args ); $class = $this->get_field_class( $args, 'large-text' ); $placeholder = $this->get_placeholder_attribute( $args ); $attributes = $this->get_boolean_attributes( $args ); $field_attributes = $this->get_field_attributes( $args ); $html = sprintf( '', $field_attributes['field_id'], $field_attributes['field_name'], esc_textarea( stripslashes( $value ) ), $class, $attributes, $placeholder ); $html .= $this->get_field_description( $args ); /** * This filter has been defined in class-settings-api.php */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Display CSS fields. * * @param array $args Array of arguments. * @return void */ public function callback_css( $args ) { $this->callback_textarea( $args ); } /** * Display HTML fields. * * @param array $args Array of arguments. * @return void */ public function callback_html( $args ) { $this->callback_textarea( $args ); } /** * Get disabled attribute for pro/premium fields. * * @param array $args Field arguments. * @return string Disabled attribute or empty string. */ protected function get_disabled_attribute( $args ) { return ( ! empty( $args['disabled'] ) || ! empty( $args['pro'] ) ) ? ' disabled="disabled"' : ''; } /** * Display checkboxes. * * @param array $args Array of arguments. * @return void */ public function callback_checkbox( $args ) { $value = $this->get_field_value( $args ); $checked = ! empty( $value ) ? checked( 1, $value, false ) : ''; $default = isset( $args['default'] ) ? (int) $args['default'] : ''; $disabled = $this->get_disabled_attribute( $args ); $field_attributes = $this->get_field_attributes( $args ); $html = sprintf( '', $field_attributes['field_name'] ); $html .= sprintf( '', $field_attributes['field_id'], $field_attributes['field_name'], $checked, $disabled ); $checkbox_modified = $this->translation_strings['checkbox_modified'] ?? 'Modified from default setting'; $html .= ( (bool) $value !== (bool) $default ) ? '' . $checkbox_modified . '' : ''; $html .= $this->get_field_description( $args ); /** * This filter has been defined in class-settings-api.php */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Multicheck Callback * * Renders multiple checkboxes. * * @param array $args Array of arguments. * @return void */ public function callback_multicheck( $args ) { $html = ''; $value = $this->get_field_value( $args ); $value_array = wp_parse_list( $value ); $disabled = $this->get_disabled_attribute( $args ); $field_attributes = $this->get_field_attributes( $args ); if ( ! empty( $args['options'] ) ) { $html .= sprintf( '', $field_attributes['field_name'] ); foreach ( $args['options'] as $key => $option ) { if ( in_array( $key, $value_array, true ) ) { $enabled = $key; } else { $enabled = null; } $option_id = $field_attributes['field_id'] . '-' . sanitize_key( $key ); $option_name = $field_attributes['field_name'] . '[' . sanitize_key( $key ) . ']'; $html .= sprintf( ' ', $option_name, $option_id, esc_attr( $key ), checked( $key, $enabled, false ), $disabled ); $html .= sprintf( '
', $option_id, wp_kses_post( $option ) ); } } $html .= $this->get_field_description( $args ); /** * This filter has been defined in class-settings-api.php */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Radio Callback * * Renders radio boxes. * * @param array $args Array of arguments. * @return void */ public function callback_radio( $args ) { $html = ''; $value = $this->get_field_value( $args ); $disabled = $this->get_disabled_attribute( $args ); $field_attributes = $this->get_field_attributes( $args ); foreach ( $args['options'] as $key => $option ) { $option_id = $field_attributes['field_id'] . '-' . $key; $html .= sprintf( ' ', $field_attributes['field_name'], $option_id, $key, checked( $value, $key, false ), $disabled ); $html .= sprintf( '
', $option_id, wp_kses_post( $option ) ); } $html .= $this->get_field_description( $args ); /** * This filter has been defined in class-settings-api.php */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Radio callback with description. * * Renders radio boxes with each item having it separate description. * * @param array $args Array of arguments. * @return void */ public function callback_radiodesc( $args ) { $html = ''; $value = $this->get_field_value( $args ); $disabled = $this->get_disabled_attribute( $args ); $field_attributes = $this->get_field_attributes( $args ); foreach ( $args['options'] as $option ) { $option_id = $field_attributes['field_id'] . '-' . $option['id']; $html .= sprintf( ' ', $field_attributes['field_name'], $option_id, $option['id'], checked( $value, $option['id'], false ), $disabled ); $html .= sprintf( '', $option_id, wp_kses_post( $option['name'] ), wp_kses_post( $option['description'] ) ); $html .= '
'; } $html .= $this->get_field_description( $args ); /** * This filter has been defined in class-settings-api.php */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Radio callback with description. * * Renders radio boxes with each item having it separate description. * * @param array $args Array of arguments. * @return void */ public function callback_thumbsizes( $args ) { $html = ''; $thumb_size = $this->prefix . '_thumbnail'; if ( ! isset( $args['options'][ $thumb_size ] ) ) { $args['options'][ $thumb_size ] = array( 'name' => $thumb_size, 'width' => call_user_func_array( $this->prefix . '_get_option', array( 'thumb_width', 150 ) ), 'height' => call_user_func_array( $this->prefix . '_get_option', array( 'thumb_height', 150 ) ), 'crop' => call_user_func_array( $this->prefix . '_get_option', array( 'thumb_crop', true ) ), ); } $value = $this->get_field_value( $args ); $field_attributes = $this->get_field_attributes( $args ); foreach ( $args['options'] as $name => $option ) { $option_id = $field_attributes['field_id'] . '-' . $name; $html .= sprintf( ' ', $field_attributes['field_name'], $option_id, $name, checked( $value, $name, false ) ); $html .= sprintf( '
', $option_id, wp_kses_post( $name ), (int) $option['width'], (int) $option['height'], (bool) $option['crop'] ? ' cropped' : '' ); } $html .= $this->get_field_description( $args ); /** * This filter has been defined in class-settings-api.php */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Number Callback * * Renders number fields. * * @param array $args Array of arguments. * @return void */ public function callback_number( $args ) { $value = $this->get_field_value( $args ); $max = isset( $args['max'] ) ? intval( $args['max'] ) : 999999; $min = isset( $args['min'] ) ? intval( $args['min'] ) : 0; $step = isset( $args['step'] ) ? intval( $args['step'] ) : 1; $size = $args['size'] ?? 'regular'; $placeholder = $this->get_placeholder_attribute( $args ); $attributes = $this->get_boolean_attributes( $args ); $field_attributes = $this->get_field_attributes( $args ); $html = sprintf( '', esc_attr( (string) $step ), esc_attr( (string) $max ), esc_attr( (string) $min ), sanitize_html_class( $size ) . '-text', $field_attributes['field_id'], $field_attributes['field_name'], esc_attr( stripslashes( $value ) ), $placeholder, $attributes ); $html .= $this->get_field_description( $args ); /** * This filter has been defined in class-settings-api.php */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Select Callback * * Renders select fields. * * @param array $args Array of arguments. * @return void */ public function callback_select( $args ) { $value = $this->get_field_value( $args ); $class = $this->get_field_class( $args ); $required = ( isset( $args['required'] ) && true === $args['required'] ) ? ' required' : ''; $attributes = $this->get_disabled_attribute( $args ) . $required . $this->build_field_attributes( $args ); if ( isset( $args['chosen'] ) ) { $class .= ' chosen'; } $field_attributes = $this->get_field_attributes( $args ); $html = sprintf( ''; $html .= $this->get_field_description( $args ); /** * This filter has been defined in class-settings-api.php */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Display posttypes fields. * * @param array $args Array of arguments. * @return void */ public function callback_posttypes( $args ) { $html = ''; $options = $this->get_field_value( $args ); $disabled = $this->get_disabled_attribute( $args ); // If post_types contains a query string then parse it with wp_parse_args. if ( is_string( $options ) && strpos( $options, '=' ) ) { $post_types = wp_parse_args( $options ); } else { $post_types = wp_parse_list( $options ); } $wp_post_types = get_post_types( array( 'public' => true, ), 'objects' ); $posts_types_inc = array_intersect( wp_list_pluck( $wp_post_types, 'name' ), $post_types ); $field_attributes = $this->get_field_attributes( $args ); $html .= sprintf( '', $field_attributes['field_name'] ); foreach ( $wp_post_types as $wp_post_type ) { $option_id = $field_attributes['field_id'] . '-' . esc_attr( $wp_post_type->name ); $option_name = $field_attributes['field_name'] . '[' . esc_attr( $wp_post_type->name ) . ']'; $html .= sprintf( '
', $option_id, $option_name, esc_attr( $wp_post_type->name ), checked( true, in_array( $wp_post_type->name, $posts_types_inc, true ), false ), $disabled, esc_html( $wp_post_type->label ) ); } $html .= $this->get_field_description( $args ); /** * This filter has been defined in class-settings-api.php */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Display taxonomies fields. * * @param array $args Array of arguments. * @return void */ public function callback_taxonomies( $args ) { $html = ''; $options = $this->get_field_value( $args ); // If taxonomies contains a query string then parse it with wp_parse_args. if ( is_string( $options ) && strpos( $options, '=' ) ) { $taxonomies = wp_parse_args( $options ); } else { $taxonomies = wp_parse_list( $options ); } /* Fetch taxonomies */ $argsc = array( 'public' => true, ); $output = 'objects'; $operator = 'and'; $wp_taxonomies = get_taxonomies( $argsc, $output, $operator ); $taxonomies_inc = array_intersect( wp_list_pluck( (array) $wp_taxonomies, 'name' ), $taxonomies ); $field_attributes = $this->get_field_attributes( $args ); $html .= sprintf( '', $field_attributes['field_name'] ); foreach ( $wp_taxonomies as $wp_taxonomy ) { $option_id = $field_attributes['field_id'] . '-' . esc_attr( $wp_taxonomy->name ); $option_name = $field_attributes['field_name'] . '[' . esc_attr( $wp_taxonomy->name ) . ']'; $html .= sprintf( '
', $option_id, $option_name, esc_attr( $wp_taxonomy->name ), checked( true, in_array( $wp_taxonomy->name, $taxonomies_inc, true ), false ), esc_html( $wp_taxonomy->labels->name ) ); } $html .= $this->get_field_description( $args ); /** * This filter has been defined in class-settings-api.php */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Displays a rich text textarea for a settings field. * * @param array $args Array of arguments. */ public function callback_wysiwyg( $args ) { $value = $this->get_field_value( $args ); $size = $args['size'] ?? '500px'; $field_attributes = $this->get_field_attributes( $args ); // wp_editor requires a unique ID without brackets. $editor_id = sanitize_key( str_replace( array( '[', ']' ), array( '-', '' ), $field_attributes['field_id'] ) ); printf( '
', esc_attr( $size ) ); $editor_settings = array( 'teeny' => true, 'textarea_name' => $field_attributes['field_name'], 'textarea_rows' => 10, ); if ( isset( $args['options'] ) && is_array( $args['options'] ) ) { $editor_settings = array_merge( $editor_settings, $args['options'] ); } wp_editor( $value, $editor_id, $editor_settings ); printf( '
' ); echo wp_kses_post( $this->get_field_description( $args ) ); } /** * Displays a file upload field for a settings field. * * @param array $args Array of arguments. */ public function callback_file( $args ) { $value = $this->get_field_value( $args ); $size = sanitize_html_class( $args['size'] ?? 'regular' ); $class = $this->get_field_class( $args ); $label = $args['options']['button_label'] ?? $this->translation_strings['button_label']; $field_attributes = $this->get_field_attributes( $args ); $html = sprintf( '', $class . ' ' . $size . '-text file-url', $field_attributes['field_id'], $field_attributes['field_name'], esc_attr( $value ) ); $html .= sprintf( '', esc_attr( $label ) ); $html .= $this->get_field_description( $args ); /** * This filter has been defined in class-settings-api.php */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Displays a password field for a settings field. * * @param array $args Array of arguments. */ public function callback_password( $args ) { $value = $this->get_field_value( $args ); $size = sanitize_html_class( $args['size'] ?? 'regular' ); $class = $this->get_field_class( $args ); $field_attributes = $this->get_field_attributes( $args ); $html = sprintf( '', "$class $size-text", $field_attributes['field_id'], $field_attributes['field_name'], esc_attr( $value ), ! empty( $value ) ? 'placeholder="' . esc_attr( $this->translation_strings['previous_saved'] ) . '"' : '' ); $html .= $this->get_field_description( $args ); /** * This filter has been defined in class-settings-api.php */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Callback for repeater field. * * @param array $args Array of arguments. * @return void */ public function callback_repeater( $args ) { $raw_value = $this->get_field_value( $args ); $value = ! empty( $raw_value ) && is_array( $raw_value ) ? $raw_value : array(); $class = $this->get_field_class( $args ); $attributes = $this->get_boolean_attributes( $args ) . $this->build_field_attributes( $args ); $data_index = (string) count( $value ); $live_update_field = ! empty( $args['live_update_field'] ) ? $args['live_update_field'] : 'name'; $fallback_title = ! empty( $args['new_item_text'] ) ? $args['new_item_text'] : $this->translation_strings['repeater_new_item']; $live_update_options = ! empty( $args['live_update_field_options'] ) && is_array( $args['live_update_field_options'] ) ? $args['live_update_field_options'] : array(); ob_start(); ?>
data-live-update-field-options="" data-unique-field="" >
$item ) { $this->render_repeater_item( $args, $index, $item ); } } ?>
get_field_description( $args ); /** * This filter has been defined in class-settings-api.php */ echo wp_kses( apply_filters( $this->prefix . '_after_setting_output', $html, $args ), $this->get_allowed_html() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Render a single repeater item. * * @param array $args Repeater field arguments. * @param string|int $index Current item index. * @param array|null $item Item data if exists. * @return void */ public function render_repeater_item( $args, $index, $item = null ) { if ( empty( $args['fields'] ) || ! is_array( $args['fields'] ) ) { return; } $fallback_title = ! empty( $args['new_item_text'] ) ? $args['new_item_text'] : $this->translation_strings['repeater_new_item']; // Generate or retrieve unique row ID. $item_id = ''; if ( is_array( $item ) && isset( $item['row_id'] ) ) { $item_id = $item['row_id']; } elseif ( '{{INDEX}}' !== $index ) { // For existing items without row_id, generate a persistent one. $item_id = 'row_' . md5( $args['id'] . '_' . $index ); } else { // For new items, use a placeholder that will be replaced. $item_id = '{{ROW_ID}}'; } ?>
▼
get_field_value( $args ); $decrypted_key = Settings_API::decrypt_api_key( $encrypted_key ); $args['value'] = $decrypted_key ? str_repeat( '*', strlen( $decrypted_key ) - 4 ) . substr( $decrypted_key, -4 ) : ''; $this->callback_text( $args ); } }