Files
2026-04-28 15:13:50 +02:00

1222 lines
36 KiB
PHP

<?php
namespace WPO\IPS\Settings;
use WPO\IPS\Documents\SequentialNumberStore;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
if ( ! class_exists( '\\WPO\\IPS\\Settings\\SettingsCallbacks' ) ) :
class SettingsCallbacks {
protected static ?self $_instance = null;
/**
* Instance of this class.
*/
public static function instance(): ?self {
if ( is_null( self::$_instance ) ) {
self::$_instance = new self();
}
return self::$_instance;
}
/**
* Section null callback.
*
* @return void
*/
public function section(): void {}
/**
* Debug section callback.
*
* @return void
*/
public function debug_section(): void {
echo wp_kses_post( '<strong>' . __( 'Warning!', 'woocommerce-pdf-invoices-packing-slips' ) . '</strong>' . ' ' .
__( 'The settings below are meant for debugging/development only. Do not use them on a live website!' , 'woocommerce-pdf-invoices-packing-slips' ) );
}
/**
* Custom fields section callback.
*
* @return void
*/
public function custom_fields_section(): void {
echo wp_kses_post( sprintf(
/* translators: %s Modern (Premium) */
__( 'These are used for the (optional) footer columns in the %s template, but can also be used for other elements in your custom template.' , 'woocommerce-pdf-invoices-packing-slips' ),
'<em>Modern (Premium)</em>'
) );
}
/**
* HTML section callback.
*
* @param array $args Field arguments.
* @return void
*/
public function html_section( array $args ): void {
extract( $this->normalize_settings_args( $args ) );
// output HTML
echo wp_kses_post( $html );
}
/**
* Checkbox callback.
*
* args:
* option_name - name of the main option
* id - key of the setting
* value - value if not 1 (optional)
* default - default setting (optional)
* description - description (optional)
* custom_attributes - custom attributes (optional)
*
* @param array $args Field arguments.
* @return void
*/
public function checkbox( array $args ): void {
extract( $this->normalize_settings_args( $args ) );
// output checkbox
printf(
'<input type="checkbox" id="%1$s" name="%2$s" value="%3$s" %4$s %5$s %6$s/>',
esc_attr( $id ),
esc_attr( $setting_name ),
esc_attr( $value ),
checked( $value, $current, false ),
! empty( $disabled ) ? 'disabled="disabled"' : '',
wp_kses_post( $custom_attributes )
);
if ( ! empty( $title ) ) {
printf(
'<label for="%1$s">%2$s</label>',
esc_attr( $id ),
esc_html( $title )
);
}
// print store empty input if true
if ( $store_unchecked ) {
printf(
'<input type="hidden" name="%s[wpo_wcpdf_setting_store_empty][]" value="%s"/>',
esc_attr( $option_name ),
esc_attr( $id )
);
}
// output description.
if ( ! empty( $description ) ) {
printf(
'<p class="description">%s</p>',
wp_kses_post( $description )
);
}
}
/**
* Text input callback.
*
* args:
* title - secondary title of the input (optional)
* option_name - name of the main option
* id - key of the setting
* size - size of the text input (em)
* default - default setting (optional)
* description - description (optional)
* type - type (optional)
* custom_attributes - custom attributes (optional)
*
* @param array $args Field arguments.
* @return void
*/
public function text_input( array $args ): void {
extract( $this->normalize_settings_args( $args ) );
if ( empty( $type ) ) {
$type = 'text';
}
if ( ! empty( $action_button ) ) {
echo '<div class="wpo-wcpdf-input-wrapper input ', esc_attr( $id ), '">';
}
if ( ! empty( $title ) ) {
printf(
'<label for="%1$s">%2$s</label>',
esc_attr( $id ),
esc_html( $title )
);
}
$size = ! empty( $size ) ? sprintf( 'size="%s"', esc_attr( $size ) ) : '';
printf(
'<input type="%1$s" id="%2$s" name="%3$s" value="%4$s" %5$s placeholder="%6$s" %7$s %8$s/>',
esc_attr( $type ),
esc_attr( $id ),
esc_attr( $setting_name ),
esc_attr( $current ),
esc_attr( $size ),
esc_attr( $placeholder ),
! empty( $disabled ) ? 'disabled="disabled"' : '',
wp_kses_post( $custom_attributes )
);
// Output action button.
if ( ! empty( $action_button ) ) {
$this->output_action_button( $action_button, $id );
echo '</div>';
}
// output description.
if ( ! empty( $description ) ) {
printf(
'<p class="description">%s</p>',
wp_kses_post( $description )
);
}
}
/**
* URL input callback.
*
* args:
* option_name - name of the main option
* id - key of the setting
* size - size of the text input (em)
* default - default setting (optional)
* description - description (optional)
* type - type (optional)
* custom_attributes - custom attributes (optional)
*
* @param array $args Field arguments.
* @return void
*/
public function url_input( array $args ): void {
extract( $this->normalize_settings_args( $args ) );
if ( empty( $type ) ) {
$type = 'url';
}
$size = ! empty( $size ) ? sprintf( 'size="%s"', esc_attr( $size ) ) : '';
printf(
'<input type="%1$s" id="%2$s" name="%3$s" value="%4$s" %5$s placeholder="%6$s" %7$s %8$s/>',
esc_attr( $type ),
esc_attr( $id ),
esc_attr( $setting_name ),
esc_attr( $current ),
esc_attr( $size ),
esc_attr( $placeholder ),
! empty( $disabled ) ? 'disabled="disabled"' : '',
wp_kses_post( $custom_attributes )
);
// output description.
if ( ! empty( $description ) ) {
printf(
'<p class="description">%s</p>',
wp_kses_post( $description )
);
}
}
/**
* Email input callback.
*
* args:
* option_name - name of the main option
* id - key of the setting
* size - size of the text input (em)
* default - default setting (optional)
* description - description (optional)
* type - type (optional)
* custom_attributes - custom attributes (optional)
*
* @param array $args Field arguments.
* @return void
*/
public function email_input( array $args ): void {
extract( $this->normalize_settings_args( $args ) );
if ( empty( $type ) ) {
$type = 'email';
}
$size = ! empty( $size ) ? sprintf( 'size="%s"', esc_attr( $size ) ) : '';
printf(
'<input type="%1$s" id="%2$s" name="%3$s" value="%4$s" %5$s placeholder="%6$s" %7$s %8$s/>',
esc_attr( $type ),
esc_attr( $id ),
esc_attr( $setting_name ),
esc_attr( sanitize_email( $current ) ),
esc_attr( $size ),
esc_attr( $placeholder ),
! empty( $disabled ) ? 'disabled="disabled"' : '',
wp_kses_post( $custom_attributes )
);
// output description.
if ( ! empty( $description ) ) {
printf(
'<p class="description">%s</p>',
wp_kses_post( $description )
);
}
}
/**
* Combined checkbox & text input callback.
*
* args:
* option_name - name of the main option
* id - key of the setting
* value - value if not 1 (optional)
* default - default setting (optional)
* description - description (optional)
*
* @param array $args Field arguments.
* @return void
*/
public function checkbox_text_input( array $args ): void {
$args = $this->normalize_settings_args( $args );
extract( $args );
unset( $args['description'] ); // already extracted, should only be used here
// get checkbox
ob_start();
$this->checkbox( $args );
$checkbox = ob_get_clean();
// get text input for insertion in wrapper
$input_args = array(
'id' => $args['text_input_id'],
'default' => isset( $args['text_input_default'] ) ? (string) $args['text_input_default'] : null,
'size' => isset( $args['text_input_size'] ) ? $args['text_input_size'] : null,
) + $args;
unset( $input_args['current'] );
unset( $input_args['setting_name'] );
ob_start();
$this->text_input( $input_args );
$text_input = ob_get_clean();
$allowed_html = array(
'input' => array(
'type' => true,
'name' => true,
'id' => true,
'value' => true,
'class' => true,
'placeholder' => true,
'disabled' => true,
'checked' => true,
'size' => true,
),
);
if ( ! empty( $text_input_wrap ) ) {
$text_input = sprintf( $text_input_wrap, $text_input );
}
echo wp_kses( "{$checkbox} {$text_input}", $allowed_html );
// output description.
if ( ! empty( $description ) ) {
printf(
'<p class="description">%s</p>',
wp_kses_post( $description )
);
}
}
/**
* Single text option (not part of any settings array)
*
* args:
* option_name - name of the main option
* id - key of the setting
* default - default setting (optional)
* description - description (optional)
* custom_attributes - custom attributes (optional)
*
* @param array $args Field arguments.
* @return void
*/
public function singular_text_element( array $args ): void {
$args = $this->normalize_settings_args( $args );
extract( $args );
$size = $size ?? '25';
$class = isset( $translatable ) && true === $translatable ? 'translatable' : '';
$option = get_option( $option_name ?? '' );
if ( ! empty( $option ) ) {
$current = $option;
} else {
$current = $default ?? '';
}
printf(
'<input type="text" id="%1$s" name="%2$s" value="%3$s" size="%4$s" class="%5$s" %6$s/>',
esc_attr( $id ),
esc_attr( $option_name ),
esc_attr( $current ),
esc_attr( $size ),
esc_attr( $class ),
wp_kses_post( $custom_attributes )
);
// output description.
if ( ! empty( $description ) ) {
printf(
'<p class="description">%s</p>',
wp_kses_post( $description )
);
}
}
/**
* Textarea callback.
*
* args:
* title - secondary title of the input (optional)
* option_name - name of the main option
* id - key of the setting
* width - width of the text input (em)
* height - height of the text input (lines)
* default - default setting (optional)
* description - description (optional)
* custom_attributes - custom attributes (optional)
*
* @param array $args Field arguments.
* @return void
*/
public function textarea( array $args ): void {
extract( $this->normalize_settings_args( $args ) );
printf(
'<textarea id="%1$s" name="%2$s" cols="%4$s" rows="%5$s" placeholder="%6$s" %7$s/>%3$s</textarea>',
esc_attr( $id ),
esc_attr( $setting_name ),
esc_textarea( $current ),
esc_attr( $width ),
esc_attr( $height ),
esc_attr( $placeholder ),
wp_kses_post( $custom_attributes )
);
// output description.
if ( ! empty( $description ) ) {
printf(
'<p class="description">%s</p>',
wp_kses_post( $description )
);
}
}
/**
* Select element callback.
*
* args:
* title - secondary title of the input (optional)
* setting_name - name of the main setting
* id - key of the setting
* multiple - whether the select is multiple (optional)
* options - array of options for the select
* current - current value(s) of the setting
* disabled - whether the select is disabled (optional)
* custom_attributes - custom attributes (optional)
*
* @param array $args Field arguments.
* @return void
*/
public function select( array $args ): void {
extract( $this->normalize_settings_args( $args ) );
if ( ! empty( $action_button ) ) {
echo '<div class="wpo-wcpdf-input-wrapper select ', esc_attr( $id ), '">';
}
if ( ! empty( $title ) ) {
printf(
'<label for="%1$s">%2$s</label>',
esc_attr( $id ),
esc_html( $title )
);
}
if ( ! empty( $enhanced_select ) ) {
if ( ! empty( $multiple ) ) {
$setting_name = "{$setting_name}[]";
$multiple = 'multiple=multiple';
} else {
$multiple = '';
}
$placeholder = ! empty( $placeholder ) ? esc_attr( $placeholder ) : '';
$title = ! empty( $title ) ? esc_attr( $title ) : '';
$class = 'wc-enhanced-select wpo-wcpdf-enhanced-select';
$css = 'width:400px';
printf(
'<select id="%1$s" name="%2$s" data-placeholder="%3$s" title="%4$s" class="%5$s" style="%6$s" %7$s %8$s %9$s>',
esc_attr( $id ),
esc_attr( $setting_name ),
esc_attr( $placeholder ),
esc_attr( $title ),
esc_attr( $class ),
esc_attr( $css ),
esc_attr( $multiple ),
! empty( $disabled ) ? 'disabled="disabled"' : '',
wp_kses_post( $custom_attributes )
);
} else {
printf(
'<select id="%1$s" name="%2$s" %3$s %4$s>',
esc_attr( $id ),
esc_attr( $setting_name ),
! empty( $disabled ) ? 'disabled="disabled"' : '',
wp_kses_post( $custom_attributes )
);
}
if ( ! empty( $options_callback ) ) {
$options = isset( $options_callback_args ) ? call_user_func_array( $options_callback, $options_callback_args ) : call_user_func( $options_callback );
}
foreach ( $options as $key => $label ) {
// Normalize both sides to string
$key_str = (string) $key;
// Determine if selected (works for both single and multiple)
if ( ! empty( $multiple ) && is_array( $current ) ) {
$is_selected = in_array( $key_str, array_map( 'strval', $current ), true );
$selected_attr = $is_selected ? ' selected="selected"' : '';
} else {
$selected_attr = selected( (string) $current, $key_str, false );
}
printf(
'<option value="%s"%s>%s</option>',
esc_attr( $key_str ),
esc_attr( $selected_attr ),
esc_html( $label )
);
}
echo '</select>';
// Output action button.
if ( ! empty( $action_button ) ) {
$this->output_action_button( $action_button, $id );
echo '</div>';
}
// output description.
if ( ! empty( $description ) ) {
printf(
'<p class="description">%s</p>',
wp_kses_post( $description )
);
}
if ( ! empty( $custom ) ) {
printf(
'<div class="%1$s_custom custom">',
esc_attr( $id )
);
if ( is_callable( array( $this, $custom['type'] ) ) ) {
$this->{$custom['type']}( $custom['args'] );
}
echo '</div>';
$custom_option = ! empty( $custom['custom_option'] ) ? $custom['custom_option'] : 'custom';
?>
<script>
jQuery( function( $ ) {
function check_<?php echo esc_attr( $id ); ?>_custom() {
var custom = $( '#<?php echo esc_attr( $id ); ?>' ).val();
if ( custom == '<?php echo esc_attr( $custom_option ); ?>' ) {
$( '.<?php echo esc_attr( $id ); ?>_custom' ).show();
} else {
$( '.<?php echo esc_attr( $id ); ?>_custom' ).hide();
}
}
check_<?php echo esc_attr( $id ); ?>_custom();
$( '#<?php echo esc_attr( $id ); ?>' ).on( 'change', function() {
check_<?php echo esc_attr( $id ); ?>_custom();
} );
} );
</script>
<?php
}
}
/**
* Radio button callback.
*
* args:
* title - secondary title of the input (optional)
* setting_name - name of the main setting
* id - key of the setting
* options - array of options for the radio buttons
* current - current value of the setting
* disabled - whether the radio buttons are disabled (optional)
* custom_attributes - custom attributes (optional)
*
* @param array $args Field arguments.
* @return void
*/
public function radio_button( array $args ): void {
extract( $this->normalize_settings_args( $args ) );
if ( ! empty( $options_callback ) ) {
$options = isset( $options_callback_args ) ? call_user_func_array( $options_callback, $options_callback_args ) : call_user_func( $options_callback );
}
foreach ( $options as $key => $label ) {
printf(
'<input type="radio" class="radio" id="%1$s[%3$s]" name="%2$s" value="%3$s"%4$s %5$s />',
esc_attr( $id ),
esc_attr( $setting_name ),
esc_attr( $key ),
checked( $current, $key, false ),
wp_kses_post( $custom_attributes )
);
printf(
'<label for="%1$s[%2$s]"> %3$s</label><br>',
esc_attr( $id ),
esc_attr( $key ),
esc_html( $label )
);
}
// output description.
if ( ! empty( $description ) ) {
printf(
'<p class="description">%s</p>',
wp_kses_post( $description )
);
}
}
/**
* Multiple text element callback.
*
* args:
* id - key of the setting
* setting_name - name of the main setting
* fields_callback - callback function to get the fields
* fields_callback_args - arguments for the fields callback (optional)
* current - current values of the setting
* header - header for the table (optional)
* description - description for the table (optional)
* custom_attributes - custom attributes for the input fields (optional)
*
* @param array $args Field arguments.
* @return void
*/
public function multiple_text_input( array $args ): void {
extract( $this->normalize_settings_args( $args ) );
if ( ! empty( $fields_callback ) ) {
$fields = isset( $fields_callback_args ) ? call_user_func_array( $fields_callback, $fields_callback_args ) : call_user_func( $fields_callback );
}
printf( '<table class="%s multiple-text-input">', esc_attr( $id ) );
if ( ! empty( $header ) ) {
echo wp_kses_post( "<tr><td><strong>{$header}</strong>:</td></tr>" );
}
foreach ( $fields as $name => $field ) {
echo '<tr>';
$size = $field['size'];
$placeholder = isset( $field['placeholder'] ) ? $field['placeholder'] : '';
$field_description = ! empty( $field['description'] ) ? $field['description']: '';
// output field label
if ( isset( $field['label'] ) ) {
printf(
'<td class="label"><label for="%1$s_%2$s">%3$s:</label></td>',
esc_attr( $id ),
esc_attr( $name ),
esc_html( $field['label'] )
);
} else {
echo '<td></td>';
}
$field_current = isset( $current[ $name ] ) ? $current[ $name ] : '';
$type = isset( $field['type'] ) ? $field['type'] : 'text';
// output field
printf(
'<td><input type="%1$s" id="%2$s_%4$s" name="%3$s[%4$s]" value="%5$s" size="%6$s" placeholder="%7$s" %8$s/></td>',
esc_attr( $type ),
esc_attr( $id ),
esc_attr( $setting_name ),
esc_attr( $name ),
esc_attr( $field_current ),
esc_attr( $size ),
esc_attr( $placeholder ),
wp_kses_post( $custom_attributes )
);
// field description.
if ( ! empty( $field_description ) ) {
echo '<td>' . wp_kses_post( wc_help_tip( $field_description, true ) ) . '</td>';
} else {
echo '<td></td>';
}
echo '</tr>';
}
echo "</table>";
// group description.
if ( ! empty( $description ) ) {
printf(
'<p class="description">%s</p>',
wp_kses_post( $description )
);
}
}
/**
* Multiple text element callback.
*
* args:
* id - key of the setting
* setting_name - name of the main setting
* fields_callback - callback function to get the fields
* fields_callback_args - arguments for the fields callback (optional)
* current - current values of the setting
* header - header for the table (optional)
* description - description for the table (optional)
* custom_attributes - custom attributes for the input fields (optional)
*
* @param array $args Field arguments.
* @return void
*/
public function multiple_checkboxes( array $args ): void {
extract( $this->normalize_settings_args( $args ) );
if ( ! empty( $fields_callback ) ) {
$fields = isset( $fields_callback_args ) ? call_user_func_array( $fields_callback, $fields_callback_args ) : call_user_func( $fields_callback );
}
foreach ( $fields as $name => $label ) {
$field_current = isset( $current[ $name ] ) ? $current[ $name ] : '';
// output checkbox
printf(
'<input type="checkbox" id="%1$s_%3$s" name="%2$s[%3$s]" value="%4$s"%5$s %6$s/>',
esc_attr( $id ),
esc_attr( $setting_name ),
esc_attr( $name ),
esc_attr( $value ),
checked( $value, $field_current, false ),
wp_kses_post( $custom_attributes )
);
// output field label
printf(
'<label for="%1$s_%2$s">%3$s</label><br>',
esc_attr( $id ),
esc_attr( $name ),
esc_html( $label )
);
}
// output description.
if ( ! empty( $description ) ) {
printf(
'<p class="description">%s</p>',
wp_kses_post( $description )
);
}
}
/**
* Media upload callback.
*
* args:
* id - key of the setting
* setting_name - name of the main setting
* current - current value of the setting
* uploader_title - title of the media uploader
* uploader_button_text - text of the media uploader button
* remove_button_text - text of the remove button
* description - description for the media upload field
* custom_attributes - custom attributes for the input field
*
* @param array $args Field arguments.
* @return void
*/
public function media_upload( array $args ): void {
extract( $this->normalize_settings_args( $args ) );
$setting_name = $this->append_language( $setting_name, $args );
$attachment = ! empty( $current ) ? wp_get_attachment_image_src( $current, 'full', false ) : '';
if ( ! empty( $attachment ) ) {
$general_settings = get_option( 'wpo_wcpdf_settings_general', array() );
$attachment_src = $attachment[0];
$attachment_width = $attachment[1];
$attachment_height = $attachment[2];
// check if we have the height saved on settings
$header_logo_height = ! empty( $general_settings['header_logo_height'] ) ? $general_settings['header_logo_height'] : '3cm';
if ( false !== stripos( $header_logo_height, 'mm' ) ) {
$in_height = floatval( $header_logo_height ) / 25.4;
} elseif ( false !== stripos( $header_logo_height, 'cm' ) ) {
$in_height = floatval( $header_logo_height ) / 2.54;
} elseif ( false !== stripos( $header_logo_height, 'in' ) ) {
$in_height = floatval( $header_logo_height );
} else {
// don't display resolution
}
/**
* .webp support can be disabled but still showing the image in settings.
* We should add a notice because this will display an error when redering the PDF using DOMPDF.
*/
if ( 'webp' === wp_check_filetype( $attachment_src )['ext'] && ! function_exists( 'imagecreatefromwebp' ) ) {
printf(
'<div class="notice notice-warning inline" style="display:inline-block; width:auto;"><p>%s</p></div>',
wp_kses_post(
/* translators: %1$s: file type, %2$s: System Configurations, %3$s: Advanced */
__( 'File type %1$s is not supported by your server! Please check your %2$s under the %3$s tab.', 'woocommerce-pdf-invoices-packing-slips' ),
'<strong>webp</strong>',
'<strong>' . __( 'System Configurations', 'woocommerce-pdf-invoices-packing-slips' ) . '</strong>',
'<strong>' . __( 'Advanced', 'woocommerce-pdf-invoices-packing-slips' ) . '</strong>'
)
);
}
printf(
'<img src="%1$s" style="display:block" id="img-%2$s" class="media-upload-preview"/>',
esc_attr( $attachment_src ),
esc_attr( $id )
);
if ( ! empty( $attachment_height ) && ! empty( $in_height ) ) {
$attachment_resolution = round( absint( $attachment_height ) / $in_height );
printf(
'<div class="attachment-resolution"><p class="description">%s: %sdpi</p></div>',
esc_html__( 'Image resolution', 'woocommerce-pdf-invoices-packing-slips' ),
esc_html( $attachment_resolution )
);
// warn the user if the image is unnecessarily large
if ( $attachment_resolution > 600 ) {
printf(
'<div class="attachment-resolution-warning notice notice-warning inline"><p>%s</p></div>',
esc_html__( 'The image resolution exceeds the recommended maximum of 600dpi. This will unnecessarily increase the size of your PDF files and could negatively affect performance.', 'woocommerce-pdf-invoices-packing-slips' )
);
}
}
printf(
'<span class="button wpo_remove_image_button" data-input_id="%1$s">%2$s</span> ',
esc_attr( $id ),
esc_attr( $remove_button_text )
);
}
printf(
'<input id="%1$s" name="%2$s" type="hidden" value="%3$s" data-settings_callback_args="%4$s" data-ajax_nonce="%5$s" class="media-upload-id"/>',
esc_attr( $id ),
esc_attr( $setting_name ),
esc_attr( $current ),
esc_attr( wp_json_encode( $args ) ),
esc_attr( wp_create_nonce( 'wpo_wcpdf_get_media_upload_setting_html' ) )
);
printf(
'<span class="button wpo_upload_image_button %4$s" data-uploader_title="%1$s" data-uploader_button_text="%2$s" data-remove_button_text="%3$s" data-input_id="%4$s">%2$s</span>',
esc_attr( $uploader_title ),
esc_attr( $uploader_button_text ),
esc_attr( $remove_button_text ),
esc_attr( $id )
);
// Displays option description.
if ( ! empty( $description ) ) {
printf(
'<p class="description">%s</p>',
wp_kses_post( $description )
);
}
}
/**
* Next document number edit callback.
*
* args:
* store - name of the store (e.g. 'invoice', 'packing_slip')
* size - size of the input field (optional)
* description - description of the field (optional)
* store_callback - callback function to get the store (optional)
* store_callback_args - arguments for the store callback (optional)
*
* @param array $args Field arguments.
* @return void
*/
public function next_number_edit( array $args ): void {
extract( $args ); // $store, $size, $description
if ( ! empty( $store_callback ) ) {
$store = isset( $store_callback_args ) ? call_user_func_array( $store_callback, $store_callback_args ) : call_user_func( $store_callback );
}
// SequentialNumberStore object
if ( is_object( $store ) ) {
$next_number = $store->get_next();
$store = $store->store_name;
// legacy
} else {
$number_store_method = WPO_WCPDF()->settings->get_sequential_number_store_method();
$number_store = new SequentialNumberStore( $store, $number_store_method );
$next_number = $number_store->get_next();
}
$nonce = wp_create_nonce( "wpo_wcpdf_next_{$store}" );
printf(
'<input id="next_%1$s" class="next-number-input" type="number" size="%2$s" value="%3$s" disabled="disabled" data-store="%1$s" data-nonce="%4$s"/> <span class="edit-next-number dashicons dashicons-edit"></span><span class="save-next-number button secondary" style="display:none;">%5$s</span>',
esc_attr( $store ),
esc_attr( $size ),
esc_attr( $next_number ),
esc_attr( $nonce ),
esc_html__( 'Save', 'woocommerce-pdf-invoices-packing-slips' )
);
// Displays option description.
if ( ! empty( $description ) ) {
printf(
'<p class="description">%s</p>',
wp_kses_post( $description )
);
}
}
/**
* Wrapper function to create tabs for settings in different languages
*
* args:
* option_name - name of the main option
* id - key of the setting
* callback - callback function to render the fields
* fields - array of fields to render (optional, used for multiple_text_input)
* i18n_description - description for the internationalized fields
*
* @param array $args
*
* @return void
*/
public function i18n_wrap( array $args ): void {
extract( $this->normalize_settings_args( $args ) );
$languages = wpo_wcpdf_get_multilingual_languages();
if ( ! empty( $languages ) ) {
printf(
'<div id="%s-%s-translations" class="translations">',
esc_attr( $option_name ),
esc_attr( $id )
);
?>
<ul>
<?php
foreach ( $languages as $lang_code => $language_name ) {
$translation_id = "{$option_name}_{$id}_{$lang_code}";
printf(
'<li><a href="#%s">%s</a></li>',
esc_attr( $translation_id ),
esc_html( $language_name )
);
}
?>
</ul>
<?php
foreach ( $languages as $lang_code => $language_name ) {
$translation_id = "{$option_name}_{$id}_{$lang_code}";
printf(
'<div id="%s">',
esc_attr( $translation_id )
);
$args['lang'] = $lang_code;
// don't use internationalized placeholders since they're not translated,
// to avoid confusion (user thinking they're all the same)
if ( 'multiple_text_input' === $callback ) {
foreach ( $fields as $key => $field_args ) {
if ( ! empty( $field_args['placeholder'] ) && isset( $field_args['i18n_placeholder'] ) ) {
$args['fields'][$key]['placeholder'] = '';
}
}
} else {
if ( ! empty( $args['placeholder'] ) && isset( $args['i18n_placeholder'] ) ) {
$args['placeholder'] = '';
}
}
// specific description for internationalized fields (to compensate for missing placeholder)
if ( ! empty( $args['i18n_description'] ) ) {
$args['description'] = $args['i18n_description'];
}
if ( is_array( $callback ) ) {
call_user_func( $callback, $args );
} else {
call_user_func( array( $this, $callback ), $args );
}
echo '</div>';
}
?>
</div>
<?php
} else {
$args['lang'] = 'default';
if ( is_array( $callback ) ) {
call_user_func( $callback, $args );
} else {
call_user_func( array( $this, $callback ), $args );
}
}
}
/**
* Normalize settings arguments.
*
* @param array $args Field arguments.
* @return array
*/
public function normalize_settings_args( array $args ): array {
$args['value'] = isset( $args['value'] ) ? $args['value'] : 1;
$args['placeholder'] = isset( $args['placeholder'] ) ? $args['placeholder'] : '';
$args['store_unchecked'] = isset( $args['store_unchecked'] ) && $args['store_unchecked'] ? true : false;
// Get main settings array
$option = get_option( $args['option_name'] );
if ( empty( $args['setting_name'] ) ) {
$args['setting_name'] = "{$args['option_name']}[{$args['id']}]";
}
if ( ! isset( $args['lang'] ) && ! empty( $args['translatable'] ) ) {
$args['lang'] = 'default';
}
if ( ! array_key_exists( 'current', $args ) ) {
if ( isset( $args['lang'] ) ) {
// i18n settings name
$args['setting_name'] = "{$args['setting_name']}[{$args['lang']}]";
// Copy current option value if set
if ( 'default' === $args['lang'] && ! empty( $option[ $args['id'] ] ) && ! isset( $option[ $args['id'] ]['default'] ) ) {
// We're switching back from WPML to normal
// Try English first
if ( isset( $option[ $args['id'] ]['en'] ) ) {
$args['current'] = $option[ $args['id'] ]['en'];
} elseif ( is_array( $option[ $args['id'] ] ) ) {
// Fallback to the first language if English not found
$first = array_shift( $option[ $args['id'] ] );
if ( ! empty( $first ) ) {
$args['current'] = $first;
}
} elseif ( is_string( $option[ $args['id'] ] ) ) {
$args['current'] = $option[ $args['id'] ];
} else {
// Nothing, really?
$args['current'] = '';
}
} else {
if ( isset( $option[ $args['id'] ][ $args['lang'] ] ) ) {
$args['current'] = $option[ $args['id'] ][ $args['lang'] ];
} elseif ( isset( $option[ $args['id'] ]['default'] ) ) {
$args['current'] = $option[ $args['id'] ]['default'];
} elseif ( isset( $option[ $args['id'] ] ) && ! is_array( $option[ $args['id'] ] ) ) {
$args['current'] = $option[ $args['id'] ];
}
}
} else {
// Copy current option value if set
if ( isset( $option[ $args['id'] ] ) ) {
$args['current'] = $option[ $args['id'] ];
}
}
}
// Fallback to default or empty if no value in option
if ( ! isset( $args['current'] ) ) {
$args['current'] = isset( $args['default'] ) ? $args['default'] : '';
} elseif ( empty( $args['current'] ) && isset( $args['default_if_empty'] ) && true === $args['default_if_empty'] ) {
// Force fallback if empty 'current' and 'default_if_empty' equals to true
$args['current'] = isset( $args['default'] ) ? $args['default'] : '';
}
// Normalize custom attributes
$args['custom_attributes'] = $this->normalize_custom_attributes( $args );
return $args;
}
/**
* Normalize custom attributes.
*
* @param array $args Field arguments.
* @return string
*/
public function normalize_custom_attributes( array $args ): string {
$custom_attributes = array();
if ( ! empty( $args['custom_attributes'] ) && is_array( $args['custom_attributes'] ) ) {
foreach ( $args['custom_attributes'] as $attribute => $attribute_value ) {
$custom_attributes[] = esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '"';
}
}
return ! empty( $custom_attributes ) ? implode( ' ', $custom_attributes ) : '';
}
/**
* Validate options.
*
* @param array|false|null $input Options to validate.
* @return array Validated options.
*/
public function validate( $input ): array {
$output = array(); // Create our array for storing the validated options.
if ( ! empty( $input ) && is_array( $input ) ) {
if ( ! empty( $input['wpo_wcpdf_setting_store_empty'] ) && is_array( $input['wpo_wcpdf_setting_store_empty'] ) ) {
foreach ( $input['wpo_wcpdf_setting_store_empty'] as $key ) {
if ( empty( $input[ $key ] ) ) {
$output[ $key ] = 0;
}
}
unset( $input['wpo_wcpdf_setting_store_empty'] );
}
// Loop through each of the incoming options.
foreach ( $input as $key => $value ) {
if ( is_array( $value ) ) {
foreach ( $value as $sub_key => $sub_value ) {
$output[ $key ][ $sub_key ] = $sub_value;
}
} else {
// Normalize identifiers like VAT / CoC on save.
if ( in_array( $key, array( 'vat_number', 'coc_number' ), true ) ) {
$value = $this->normalize_identifier( (string) $value );
}
$output[ $key ] = $value;
}
}
}
// Return the array processing any additional functions filtered by this action.
return apply_filters( 'wpo_wcpdf_validate_input', $output, $input );
}
/**
* Appends language at the end of the setting provided, in case the setting is translatable
* and it does not have a language set.
*
* @param string $setting Settings field that needs a language.
* @param array $args Setting arguments.
*
* @return string
*/
public function append_language( string $setting, array $args ): string {
if (
isset( $args['translatable'] ) &&
true === $args['translatable'] &&
isset( $args['lang'] ) &&
'default' !== $args['lang'] &&
! ( substr( $setting, -strlen( "[{$args['lang']}]" ) ) === "[{$args['lang']}]" )
) {
return $setting .= "[{$args['lang']}]";
} else {
return $setting;
}
}
/**
* Output the action button.
*
* @param array $action_button
* @param string $id
*
* @return void
*/
private function output_action_button( array $action_button, string $id ): void {
printf(
'<button type="button" %1$s %2$s %3$s>%4$s%5$s</button><span class="sync-tooltip"></span>',
! empty( $action_button['class'] ) ? sprintf( 'class="%s"', esc_attr( $action_button['class'] ) ) : '',
sprintf( 'id="%s"', esc_attr( $action_button['id'] ?? esc_attr( $id ) ) . '_action' ),
! empty( $action_button['title'] ) ? sprintf( 'title="%s"', esc_attr( $action_button['title'] ) ) : '',
esc_html( $action_button['text'] ),
! empty( $action_button['icon'] ) ? sprintf( '<span class="dashicons dashicons-%s"></span>', esc_attr( $action_button['icon'] ) ) : ''
);
}
/**
* Normalize identifier-like values (VAT / CoC).
*
* @param string $value
* @return string
*/
protected function normalize_identifier( string $value ): string {
$value = wp_strip_all_tags( $value );
$value = trim( $value );
// Uppercase for consistency (VAT formats often expect it).
$value = strtoupper( $value );
// Keep only A-Z and 0-9, strip spaces, dots, dashes, etc.
$value = preg_replace( '/[^A-Z0-9]/', '', $value );
return $value;
}
}
endif; // class_exists