PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 11.0.1
WooCommerce v11.0.1
11.1.0-rc.2 11.1.0-rc.1 11.1.0-beta.2 11.1.0-beta.1 11.0.1 11.0.0 11.0.0-rc.3 11.0.0-rc.2 11.0.0-rc.1 11.0.0-beta.2 11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / includes / admin / class-wc-admin-settings.php
woocommerce / includes / admin Last commit date
helper 3 weeks ago importers 1 month ago list-tables 1 month ago marketplace-suggestions 1 year ago meta-boxes 1 month ago notes 3 months ago plugin-updates 2 years ago reports 1 month ago settings 1 month ago views 1 month ago class-wc-admin-addons.php 10 months ago class-wc-admin-api-keys-table-list.php 3 years ago class-wc-admin-api-keys.php 1 month ago class-wc-admin-assets.php 1 month ago class-wc-admin-attributes.php 1 month ago class-wc-admin-brands.php 1 month ago class-wc-admin-customize.php 6 years ago class-wc-admin-dashboard-setup.php 1 month ago class-wc-admin-dashboard.php 1 month ago class-wc-admin-duplicate-product.php 1 month ago class-wc-admin-exporters.php 1 year ago class-wc-admin-help.php 2 years ago class-wc-admin-importers.php 1 year ago class-wc-admin-log-table-list.php 6 months ago class-wc-admin-marketplace-promotions.php 1 month ago class-wc-admin-menus.php 1 month ago class-wc-admin-meta-boxes.php 2 months ago class-wc-admin-notices.php 1 month ago class-wc-admin-permalink-settings.php 5 years ago class-wc-admin-pointers.php 3 years ago class-wc-admin-post-types.php 1 month ago class-wc-admin-profile.php 1 year ago class-wc-admin-reports.php 6 months ago class-wc-admin-settings.php 1 month ago class-wc-admin-setup-wizard.php 2 months ago class-wc-admin-status.php 1 year ago class-wc-admin-taxonomies.php 2 months ago class-wc-admin-upload-downloadable-product.php 2 years ago class-wc-admin-webhooks-table-list.php 2 months ago class-wc-admin-webhooks.php 1 month ago class-wc-admin.php 5 months ago wc-admin-functions.php 1 month ago wc-meta-box-functions.php 1 year ago woocommerce-legacy-reports.php 1 year ago
class-wc-admin-settings.php
1101 lines
1 <?php
2 /**
3 * WooCommerce Admin Settings Class
4 *
5 * @package WooCommerce\Admin
6 * @version 3.4.0
7 */
8
9 use Automattic\Jetpack\Constants;
10
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
16
17 /**
18 * WC_Admin_Settings Class.
19 */
20 class WC_Admin_Settings {
21
22 /**
23 * Setting pages.
24 *
25 * @var array
26 */
27 private static $settings = array();
28
29 /**
30 * Error messages.
31 *
32 * @var array
33 */
34 private static $errors = array();
35
36 /**
37 * Update messages.
38 *
39 * @var array
40 */
41 private static $messages = array();
42
43 /**
44 * Include the settings page classes.
45 */
46 public static function get_settings_pages() {
47 if ( empty( self::$settings ) ) {
48 $settings = array();
49
50 include_once __DIR__ . '/settings/class-wc-settings-page.php';
51
52 $settings[] = include_once __DIR__ . '/settings/class-wc-settings-general.php';
53 $settings[] = include_once __DIR__ . '/settings/class-wc-settings-products.php';
54 $settings[] = include_once __DIR__ . '/settings/class-wc-settings-tax.php';
55 $settings[] = include_once __DIR__ . '/settings/class-wc-settings-shipping.php';
56 $settings[] = include_once __DIR__ . '/settings/class-wc-settings-payment-gateways.php';
57 $settings[] = include_once __DIR__ . '/settings/class-wc-settings-accounts.php';
58 $settings[] = include_once __DIR__ . '/settings/class-wc-settings-emails.php';
59 $settings[] = include_once __DIR__ . '/settings/class-wc-settings-integrations.php';
60 if ( \Automattic\WooCommerce\Admin\Features\Features::is_enabled( 'launch-your-store' ) ) {
61 $settings[] = include_once __DIR__ . '/settings/class-wc-settings-site-visibility.php';
62 }
63 $settings[] = include_once __DIR__ . '/settings/class-wc-settings-point-of-sale.php';
64 $settings[] = include_once __DIR__ . '/settings/class-wc-settings-advanced.php';
65
66 self::$settings = apply_filters( 'woocommerce_get_settings_pages', $settings );
67 add_action(
68 'admin_head',
69 function () {
70 if ( function_exists( 'get_current_screen' ) ) {
71 $screen = get_current_screen();
72 if ( 'woocommerce_page_wc-settings' === $screen->id ) {
73 $screen->remove_help_tabs();
74 }
75 }
76 }
77 );
78 }
79
80 return self::$settings;
81 }
82
83 /**
84 * Save the settings.
85 */
86 public static function save() {
87 global $current_tab;
88
89 if ( ! current_user_can( 'manage_woocommerce' ) ) {
90 wp_die( esc_html__( 'You do not have permission to save settings.', 'woocommerce' ), 403 );
91 }
92
93 check_admin_referer( 'woocommerce-settings' );
94
95 // Trigger actions.
96 do_action( 'woocommerce_settings_save_' . $current_tab );
97 do_action( 'woocommerce_update_options_' . $current_tab );
98 do_action( 'woocommerce_update_options' );
99
100 self::add_message( __( 'Your settings have been saved.', 'woocommerce' ) );
101 self::check_download_folder_protection();
102
103 // Clear any unwanted data and flush rules.
104 update_option( 'woocommerce_queue_flush_rewrite_rules', 'yes' );
105 WC()->query->init_query_vars();
106 WC()->query->add_endpoints();
107
108 do_action( 'woocommerce_settings_saved' );
109
110 self::maybe_redirect_after_settings_ui_save();
111 }
112
113 /**
114 * Redirect to the requested Settings UI destination after a form-post save.
115 */
116 private static function maybe_redirect_after_settings_ui_save(): void {
117 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- The settings nonce is verified before this method is called.
118 if ( empty( $_POST['wc_settings_ui_redirect_to'] ) ) {
119 return;
120 }
121
122 $redirect_to = wp_validate_redirect(
123 esc_url_raw(
124 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- The settings nonce is verified before this method is called.
125 wp_unslash( $_POST['wc_settings_ui_redirect_to'] )
126 ),
127 ''
128 );
129
130 if ( '' === $redirect_to ) {
131 return;
132 }
133
134 wp_safe_redirect( $redirect_to );
135 exit;
136 }
137
138 /**
139 * Add a message.
140 *
141 * @param string $text Message.
142 */
143 public static function add_message( $text ) {
144 self::$messages[] = $text;
145 }
146
147 /**
148 * Add an error.
149 *
150 * @param string $text Message.
151 */
152 public static function add_error( $text ) {
153 self::$errors[] = $text;
154 }
155
156 /**
157 * Output messages + errors.
158 */
159 public static function show_messages() {
160 if ( count( self::$errors ) > 0 ) {
161 foreach ( self::$errors as $error ) {
162 echo '<div id="message" class="error inline"><p><strong>' . esc_html( $error ) . '</strong></p></div>';
163 }
164 } elseif ( count( self::$messages ) > 0 ) {
165 foreach ( self::$messages as $message ) {
166 echo '<div id="message" class="updated inline"><p><strong>' . esc_html( $message ) . '</strong></p></div>';
167 }
168 }
169 }
170
171 /**
172 * Settings page.
173 *
174 * Handles the display of the main woocommerce settings page in admin.
175 */
176 public static function output() {
177 global $current_section, $current_tab;
178
179 $suffix = Constants::is_true( 'SCRIPT_DEBUG' ) ? '' : '.min';
180
181 do_action( 'woocommerce_settings_start' );
182
183 wp_enqueue_script( 'woocommerce_settings', WC()->plugin_url() . '/assets/js/admin/settings' . $suffix . '.js', array( 'jquery', 'wp-util', 'jquery-ui-datepicker', 'jquery-ui-sortable', 'iris', 'selectWoo' ), WC()->version, true );
184
185 wp_localize_script(
186 'woocommerce_settings',
187 'woocommerce_settings_params',
188 array(
189 'i18n_nav_warning' => __( 'The changes you made will be lost if you navigate away from this page.', 'woocommerce' ),
190 'i18n_moved_up' => __( 'Item moved up', 'woocommerce' ),
191 'i18n_moved_down' => __( 'Item moved down', 'woocommerce' ),
192 'i18n_no_specific_countries_selected' => __( 'Selecting no country / region to sell to prevents from completing the checkout. Continue anyway?', 'woocommerce' ),
193 )
194 );
195
196 // Get tabs for the settings page.
197 $tabs = apply_filters( 'woocommerce_settings_tabs_array', array() );
198
199 include __DIR__ . '/views/html-admin-settings.php';
200 }
201
202 /**
203 * Get a setting from the settings API.
204 *
205 * @param string $option_name Option name.
206 * @param mixed $default Default value.
207 * @return mixed
208 */
209 public static function get_option( $option_name, $default = '' ) {
210 if ( ! $option_name ) {
211 return $default;
212 }
213
214 // Array value.
215 if ( strstr( $option_name, '[' ) ) {
216
217 parse_str( $option_name, $option_array );
218
219 // Option name is first key.
220 $option_name = current( array_keys( $option_array ) );
221
222 // Get value.
223 $option_values = get_option( $option_name, '' );
224
225 $key = key( $option_array[ $option_name ] );
226
227 if ( isset( $option_values[ $key ] ) ) {
228 $option_value = $option_values[ $key ];
229 } else {
230 $option_value = null;
231 }
232 } else {
233 // Single value.
234 $option_value = get_option( $option_name, null );
235 }
236
237 if ( is_array( $option_value ) ) {
238 $option_value = wp_unslash( $option_value );
239 } elseif ( ! is_null( $option_value ) ) {
240 $option_value = stripslashes( $option_value );
241 }
242
243 return ( null === $option_value ) ? $default : $option_value;
244 }
245
246 /**
247 * Output admin fields.
248 *
249 * Loops through the woocommerce options array and outputs each field.
250 *
251 * @param array[] $options Opens array to output.
252 */
253 public static function output_fields( $options ) {
254 foreach ( $options as $value ) {
255 if ( ! isset( $value['type'] ) ) {
256 continue;
257 }
258 if ( ! isset( $value['id'] ) ) {
259 $value['id'] = '';
260 }
261
262 // The 'field_name' key can be used when it is useful to specify an input field name that is different
263 // from the input field ID. We use the key 'field_name' because 'name' is already in use for a different
264 // purpose.
265 if ( ! isset( $value['field_name'] ) ) {
266 $value['field_name'] = $value['id'];
267 }
268 if ( ! isset( $value['title'] ) ) {
269 $value['title'] = isset( $value['name'] ) ? $value['name'] : '';
270 }
271 if ( ! isset( $value['class'] ) ) {
272 $value['class'] = '';
273 }
274 if ( ! isset( $value['css'] ) ) {
275 $value['css'] = '';
276 }
277 if ( ! isset( $value['default'] ) ) {
278 $value['default'] = '';
279 }
280 if ( ! isset( $value['desc'] ) ) {
281 $value['desc'] = '';
282 }
283 if ( ! isset( $value['desc_tip'] ) ) {
284 $value['desc_tip'] = false;
285 }
286 if ( ! isset( $value['placeholder'] ) ) {
287 $value['placeholder'] = '';
288 }
289 if ( ! isset( $value['row_class'] ) ) {
290 $value['row_class'] = '';
291 }
292 if ( ! empty( $value['row_class'] ) && substr( $value['row_class'], 0, 16 ) !== 'wc-settings-row-' ) {
293 $value['row_class'] = 'wc-settings-row-' . $value['row_class'];
294 }
295 if ( ! isset( $value['suffix'] ) ) {
296 $value['suffix'] = '';
297 }
298 if ( ! isset( $value['value'] ) ) {
299 $value['value'] = self::get_option( $value['id'], $value['default'] );
300 }
301
302 if ( ! is_null( $value['fixed_value'] ?? null ) ) {
303 $value['value'] = $value['fixed_value'];
304 }
305
306 // Custom attribute handling.
307 $custom_attributes = array();
308
309 if ( ! empty( $value['custom_attributes'] ) && is_array( $value['custom_attributes'] ) ) {
310 foreach ( $value['custom_attributes'] as $attribute => $attribute_value ) {
311 $custom_attributes[] = esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '"';
312 }
313 }
314
315 // Description handling.
316 $field_description = self::get_field_description( $value );
317 $description = $field_description['description'];
318 $tooltip_html = $field_description['tooltip_html'];
319
320 // Switch based on type.
321 switch ( $value['type'] ) {
322
323 // Section Titles.
324 case 'title':
325 if ( ! empty( $value['title'] ) ) {
326 echo '<h2>' . esc_html( $value['title'] ) . '</h2>';
327 }
328 if ( ! empty( $value['desc'] ) ) {
329 echo '<div id="' . esc_attr( sanitize_title( $value['id'] ) ) . '-description">';
330 echo wp_kses_post( wpautop( wptexturize( $value['desc'] ) ) );
331 echo '</div>';
332 }
333 echo '<table class="form-table">' . "\n\n";
334 if ( ! empty( $value['id'] ) ) {
335 do_action( 'woocommerce_settings_' . sanitize_title( $value['id'] ) );
336 }
337 break;
338
339 case 'info':
340 ?><tr class="<?php echo esc_attr( $value['row_class'] ); ?>">
341 <th scope="row" class="titledesc"><?php echo esc_html( $value['title'] ); ?></th>
342 <td style="<?php echo esc_attr( $value['css'] ); ?>">
343 <?php
344 echo wp_kses_post( wpautop( wptexturize( $value['text'] ) ) );
345 echo '</td></tr>';
346 break;
347
348 // Notice.
349 case 'notice':
350 $notice_type = $value['notice_type'] ?? 'info';
351 $notice_text = $value['text'] ?? '';
352
353 ?>
354 </table>
355 <div class="notice notice-<?php echo esc_attr( $notice_type ); ?> inline">
356 <p><?php echo wp_kses_post( $notice_text ); ?></p>
357 </div>
358 <table class="form-table" role="presentation">
359 <?php
360 break;
361
362 // Section Ends.
363 case 'sectionend':
364 if ( ! empty( $value['id'] ) ) {
365 do_action( 'woocommerce_settings_' . sanitize_title( $value['id'] ) . '_end' );
366 }
367 echo '</table>';
368 if ( ! empty( $value['id'] ) ) {
369 do_action( 'woocommerce_settings_' . sanitize_title( $value['id'] ) . '_after' );
370 }
371 break;
372
373 // Standard text inputs and subtypes like 'number'.
374 case 'text':
375 case 'password':
376 case 'datetime':
377 case 'datetime-local':
378 case 'date':
379 case 'month':
380 case 'time':
381 case 'week':
382 case 'number':
383 case 'email':
384 case 'url':
385 case 'tel':
386 $option_value = $value['value'];
387
388 ?>
389 <tr class="<?php echo esc_attr( $value['row_class'] ); ?>">
390 <th scope="row" class="titledesc">
391 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
392 </th>
393 <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ); ?>">
394 <input
395 name="<?php echo esc_attr( $value['field_name'] ); ?>"
396 id="<?php echo esc_attr( $value['id'] ); ?>"
397 type="<?php echo esc_attr( $value['type'] ); ?>"
398 style="<?php echo esc_attr( $value['css'] ); ?>"
399 value="<?php echo esc_attr( $option_value ); ?>"
400 class="<?php echo esc_attr( $value['class'] ); ?>"
401 placeholder="<?php echo esc_attr( $value['placeholder'] ); ?>"
402 <?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
403 /><?php echo esc_html( $value['suffix'] ); ?> <?php echo $description; // WPCS: XSS ok. ?>
404 </td>
405 </tr>
406 <?php
407 break;
408
409 // Color picker.
410 case 'color':
411 $option_value = $value['value'];
412
413 ?>
414 <tr class="<?php echo esc_attr( $value['row_class'] ); ?>">
415 <th scope="row" class="titledesc">
416 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
417 </th>
418 <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ); ?>">&lrm;
419 <span class="colorpickpreview" style="background: <?php echo esc_attr( $option_value ); ?>">&nbsp;</span>
420 <input
421 name="<?php echo esc_attr( $value['field_name'] ); ?>"
422 id="<?php echo esc_attr( $value['id'] ); ?>"
423 type="text"
424 dir="ltr"
425 style="<?php echo esc_attr( $value['css'] ); ?>"
426 value="<?php echo esc_attr( $option_value ); ?>"
427 class="<?php echo esc_attr( $value['class'] ); ?>colorpick"
428 placeholder="<?php echo esc_attr( $value['placeholder'] ); ?>"
429 <?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
430 />&lrm; <?php echo $description; // WPCS: XSS ok. ?>
431 <div id="colorPickerDiv_<?php echo esc_attr( $value['id'] ); ?>" class="colorpickdiv" style="z-index: 100;background:#eee;border:1px solid #ccc;position:absolute;display:none;"></div>
432 </td>
433 </tr>
434 <?php
435 break;
436
437 // Textarea.
438 case 'textarea':
439 $option_value = $value['value'];
440 $show_desc_at_end = $value['desc_at_end'] ?? false;
441
442 ?>
443 <tr class="<?php echo esc_attr( $value['row_class'] ); ?>">
444 <th scope="row" class="titledesc">
445 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
446 </th>
447 <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ); ?>">
448 <?php
449 if ( ! $show_desc_at_end ) {
450 echo wp_kses_post( $description );
451 }
452 ?>
453 <textarea
454 name="<?php echo esc_attr( $value['field_name'] ); ?>"
455 id="<?php echo esc_attr( $value['id'] ); ?>"
456 style="<?php echo esc_attr( $value['css'] ); ?>"
457 class="<?php echo esc_attr( $value['class'] ); ?>"
458 placeholder="<?php echo esc_attr( $value['placeholder'] ); ?>"
459 <?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
460 ><?php echo esc_textarea( $option_value ); // WPCS: XSS ok. ?></textarea>
461 <?php
462 if ( $show_desc_at_end ) {
463 echo wp_kses_post( $description );
464 }
465 ?>
466 </td>
467 </tr>
468 <?php
469 break;
470
471 // Select boxes.
472 case 'select':
473 case 'multiselect':
474 $option_value = $value['value'];
475
476 ?>
477 <tr class="<?php echo esc_attr( $value['row_class'] ); ?>">
478 <th scope="row" class="titledesc">
479 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
480 </th>
481 <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ); ?>">
482 <select
483 name="<?php echo esc_attr( $value['field_name'] ); ?><?php echo ( 'multiselect' === $value['type'] ) ? '[]' : ''; ?>"
484 id="<?php echo esc_attr( $value['id'] ); ?>"
485 style="<?php echo esc_attr( $value['css'] ); ?>"
486 class="<?php echo esc_attr( $value['class'] ); ?>"
487 <?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
488 <?php echo 'multiselect' === $value['type'] ? 'multiple="multiple"' : ''; ?>
489 >
490 <?php
491 foreach ( $value['options'] as $key => $val ) {
492 ?>
493 <option value="<?php echo esc_attr( $key ); ?>"
494 <?php
495
496 if ( is_array( $option_value ) ) {
497 selected( in_array( (string) $key, $option_value, true ), true );
498 } else {
499 selected( $option_value, (string) $key );
500 }
501
502 ?>
503 ><?php echo esc_html( $val ); ?></option>
504 <?php
505 }
506 ?>
507 </select> <?php echo $description; // WPCS: XSS ok. ?>
508 </td>
509 </tr>
510 <?php
511 break;
512
513 // Radio inputs.
514 case 'radio':
515 $option_value = $value['value'];
516 $disabled_values = $value['disabled'] ?? array();
517 $show_desc_at_end = $value['desc_at_end'] ?? false;
518
519 ?>
520 <tr class="<?php echo esc_attr( $value['row_class'] ); ?>">
521 <th scope="row" class="titledesc">
522 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
523 </th>
524 <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ); ?>">
525 <fieldset>
526 <?php
527 if ( ! $show_desc_at_end ) {
528 echo wp_kses_post( $description );
529 }
530 ?>
531 <ul>
532 <?php
533 foreach ( $value['options'] as $key => $val ) {
534 ?>
535 <li>
536 <label><input
537 name="<?php echo esc_attr( $value['field_name'] ); ?>"
538 value="<?php echo esc_attr( $key ); ?>"
539 type="radio"
540 <?php
541 if ( in_array( $key, $disabled_values, true ) ) {
542 echo 'disabled'; }
543 ?>
544 style="<?php echo esc_attr( $value['css'] ); ?>"
545 class="<?php echo esc_attr( $value['class'] ); ?>"
546 <?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
547 <?php checked( $key, $option_value ); ?>
548 /> <?php echo esc_html( $val ); ?></label>
549 </li>
550 <?php
551 }
552 if ( $show_desc_at_end ) {
553 echo wp_kses_post( "<p class='description description-thin'>{$description}</p>" );
554 }
555 ?>
556 </ul>
557 </fieldset>
558 </td>
559 </tr>
560 <?php
561 break;
562
563 // Checkbox input.
564 case 'checkbox':
565 $option_value = $value['value'];
566 $visibility_class = array();
567
568 if ( ! isset( $value['hide_if_checked'] ) ) {
569 $value['hide_if_checked'] = false;
570 }
571 if ( ! isset( $value['show_if_checked'] ) ) {
572 $value['show_if_checked'] = false;
573 }
574 if ( 'yes' === $value['hide_if_checked'] || 'yes' === $value['show_if_checked'] ) {
575 $visibility_class[] = 'hidden_option';
576 }
577 if ( 'option' === $value['hide_if_checked'] ) {
578 $visibility_class[] = 'hide_options_if_checked';
579 }
580 if ( 'option' === $value['show_if_checked'] ) {
581 $visibility_class[] = 'show_options_if_checked';
582 }
583 if ( $value['row_class'] ) {
584 $visibility_class[] = $value['row_class'];
585 }
586
587 $must_disable = $value['disabled'] ?? false;
588
589 if ( $must_disable ) {
590 $visibility_class[] = 'disabled';
591 }
592
593 $container_class = implode( ' ', $visibility_class );
594 $has_title = isset( $value['title'] ) && '' !== $value['title'];
595 $has_legend = isset( $value['legend'] ) && '' !== $value['legend'];
596
597 if ( ! isset( $value['checkboxgroup'] ) || 'start' === $value['checkboxgroup'] ) {
598 $has_tooltip = isset( $value['tooltip'] ) && '' !== $value['tooltip'];
599
600 $tooltip_container_class = $has_tooltip ? 'with-tooltip' : '';
601 ?>
602 <tr class="<?php echo esc_attr( $container_class ); ?>">
603 <th scope="row" class="titledesc"><?php echo esc_html( $value['title'] ); ?></th>
604 <td class="forminp forminp-checkbox <?php echo esc_html( $tooltip_container_class ); ?>">
605 <?php if ( $has_tooltip ) : ?>
606 <span class="help-tooltip"><?php echo wc_help_tip( esc_html( $value['tooltip'] ) ); ?></span>
607 <?php endif; ?>
608 <fieldset>
609 <?php
610 } else {
611 ?>
612 <fieldset class="<?php echo esc_attr( $container_class ); ?>">
613 <?php
614 }
615
616 if ( $has_title || $has_legend ) {
617 ?>
618 <legend class="<?php echo $has_legend ? '' : 'screen-reader-text'; ?>"><span><?php echo esc_html( $has_legend ? $value['legend'] : $value['title'] ); ?></span></legend>
619 <?php
620 }
621
622 ?>
623 <label for="<?php echo esc_attr( $value['id'] ); ?>">
624 <input
625 <?php echo $must_disable ? 'disabled' : ''; ?>
626 name="<?php echo esc_attr( $value['field_name'] ); ?>"
627 id="<?php echo esc_attr( $value['id'] ); ?>"
628 type="checkbox"
629 class="<?php echo esc_attr( isset( $value['class'] ) ? $value['class'] : '' ); ?>"
630 value="1"
631 <?php checked( $option_value, 'yes' ); ?>
632 <?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
633 /> <?php echo $description; // WPCS: XSS ok. ?>
634 </label> <?php echo $tooltip_html; // WPCS: XSS ok. ?>
635 <?php
636
637 if ( ! isset( $value['checkboxgroup'] ) || 'end' === $value['checkboxgroup'] ) {
638 ?>
639 </fieldset>
640 </td>
641 </tr>
642 <?php
643 } else {
644 ?>
645 </fieldset>
646 <?php
647 }
648 break;
649
650 // Image width settings. @todo deprecate and remove in 4.0. No longer needed by core.
651 case 'image_width':
652 $image_size = str_replace( '_image_size', '', $value['id'] );
653 $size = wc_get_image_size( $image_size );
654 $width = isset( $size['width'] ) ? $size['width'] : $value['default']['width'];
655 $height = isset( $size['height'] ) ? $size['height'] : $value['default']['height'];
656 $crop = isset( $size['crop'] ) ? $size['crop'] : $value['default']['crop'];
657 $disabled_attr = '';
658 $disabled_message = '';
659
660 if ( has_filter( 'woocommerce_get_image_size_' . $image_size ) ) {
661 $disabled_attr = 'disabled="disabled"';
662 $disabled_message = '<p><small>' . esc_html__( 'The settings of this image size have been disabled because its values are being overwritten by a filter.', 'woocommerce' ) . '</small></p>';
663 }
664
665 ?>
666 <tr class="<?php echo esc_attr( $value['row_class'] ); ?>">
667 <th scope="row" class="titledesc">
668 <label><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html . $disabled_message; // WPCS: XSS ok. ?></label>
669 </th>
670 <td class="forminp image_width_settings">
671
672 <input name="<?php echo esc_attr( $value['field_name'] ); ?>[width]" <?php echo $disabled_attr; // WPCS: XSS ok. ?> id="<?php echo esc_attr( $value['id'] ); ?>-width" type="text" size="3" value="<?php echo esc_attr( $width ); ?>" /> &times; <input name="<?php echo esc_attr( $value['id'] ); ?>[height]" <?php echo $disabled_attr; // WPCS: XSS ok. ?> id="<?php echo esc_attr( $value['id'] ); ?>-height" type="text" size="3" value="<?php echo esc_attr( $height ); ?>" />px
673
674 <label><input name="<?php echo esc_attr( $value['field_name'] ); ?>[crop]" <?php echo $disabled_attr; // WPCS: XSS ok. ?> id="<?php echo esc_attr( $value['id'] ); ?>-crop" type="checkbox" value="1" <?php checked( 1, $crop ); ?> /> <?php esc_html_e( 'Hard crop?', 'woocommerce' ); ?></label>
675
676 </td>
677 </tr>
678 <?php
679 break;
680
681 // Single page selects.
682 case 'single_select_page':
683 $args = array(
684 'name' => $value['field_name'],
685 'id' => $value['id'],
686 'sort_column' => 'menu_order',
687 'sort_order' => 'ASC',
688 'show_option_none' => ' ',
689 'class' => $value['class'],
690 'echo' => false,
691 'selected' => absint( $value['value'] ),
692 'post_status' => 'publish,private,draft',
693 );
694
695 if ( isset( $value['args'] ) ) {
696 $args = wp_parse_args( $value['args'], $args );
697 }
698
699 ?>
700 <tr class="single_select_page <?php echo esc_attr( $value['row_class'] ); ?>">
701 <th scope="row" class="titledesc">
702 <label><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
703 </th>
704 <td class="forminp">
705 <?php echo str_replace( ' id=', " data-placeholder='" . esc_attr__( 'Select a page&hellip;', 'woocommerce' ) . "' style='" . $value['css'] . "' class='" . $value['class'] . "' id=", wp_dropdown_pages( $args ) ); // WPCS: XSS ok. ?> <?php echo $description; // WPCS: XSS ok. ?>
706 </td>
707 </tr>
708 <?php
709 break;
710
711 case 'single_select_page_with_search':
712 $option_value = $value['value'];
713 $page = get_post( $option_value );
714
715 if ( ! is_null( $page ) ) {
716 $page = get_post( $option_value );
717 $option_display_name = sprintf(
718 /* translators: 1: page name 2: page ID */
719 __( '%1$s (ID: %2$s)', 'woocommerce' ),
720 $page->post_title,
721 $option_value
722 );
723 }
724 ?>
725 <tr class="single_select_page <?php echo esc_attr( $value['row_class'] ); ?>">
726 <th scope="row" class="titledesc">
727 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></label>
728 </th>
729 <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ); ?>">
730 <select
731 name="<?php echo esc_attr( $value['field_name'] ); ?>"
732 id="<?php echo esc_attr( $value['id'] ); ?>"
733 style="<?php echo esc_attr( $value['css'] ); ?>"
734 class="<?php echo esc_attr( $value['class'] ); ?>"
735 <?php echo implode( ' ', $custom_attributes ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
736 data-placeholder="<?php esc_attr_e( 'Search for a page&hellip;', 'woocommerce' ); ?>"
737 data-allow_clear="true"
738 data-exclude="<?php echo wc_esc_json( wp_json_encode( $value['args']['exclude'] ) ); ?>"
739 >
740 <option value=""></option>
741 <?php if ( ! is_null( $page ) ) { ?>
742 <option value="<?php echo esc_attr( $option_value ); ?>" selected="selected">
743 <?php echo wp_strip_all_tags( $option_display_name ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
744 </option>
745 <?php } ?>
746 </select> <?php echo $description; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
747 </td>
748 </tr>
749 <?php
750 break;
751
752 // Single country selects.
753 case 'single_select_country':
754 $country_setting = (string) $value['value'];
755
756 if ( strstr( $country_setting, ':' ) ) {
757 $country_setting = explode( ':', $country_setting );
758 $country = current( $country_setting );
759 $state = end( $country_setting );
760 } else {
761 $country = $country_setting;
762 $state = '*';
763 }
764 ?>
765 <tr class="<?php echo esc_attr( $value['row_class'] ); ?>">
766 <th scope="row" class="titledesc">
767 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
768 </th>
769 <td class="forminp"><select name="<?php echo esc_attr( $value['field_name'] ); ?>" id="<?php echo esc_attr( $value['id'] ); ?>" style="<?php echo esc_attr( $value['css'] ); ?>" data-placeholder="<?php esc_attr_e( 'Choose a country / region&hellip;', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Country / Region', 'woocommerce' ); ?>" class="wc-enhanced-select">
770 <?php WC()->countries->country_dropdown_options( $country, $state ); ?>
771 </select> <?php echo $description; // WPCS: XSS ok. ?>
772 </td>
773 </tr>
774 <?php
775 break;
776
777 // Country multiselects.
778 case 'multi_select_countries':
779 $selections = (array) $value['value'];
780
781 if ( ! empty( $value['options'] ) ) {
782 $countries = $value['options'];
783 } else {
784 $countries = WC()->countries->countries;
785 }
786
787 asort( $countries );
788 ?>
789 <tr class="<?php echo esc_attr( $value['row_class'] ); ?>">
790 <th scope="row" class="titledesc">
791 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
792 </th>
793 <td class="forminp">
794 <select
795 multiple="multiple"
796 name="<?php echo esc_attr( $value['field_name'] ); ?>[]"
797 id="<?php echo esc_attr( $value['id'] ); ?>"
798 style="width:350px"
799 data-placeholder="<?php esc_attr_e( 'Choose countries / regions&hellip;', 'woocommerce' ); ?>"
800 aria-label="<?php esc_attr_e( 'Country / Region', 'woocommerce' ); ?>"
801 class="wc-enhanced-select">
802 <?php
803 if ( ! empty( $countries ) ) {
804 foreach ( $countries as $key => $val ) {
805 echo '<option value="' . esc_attr( $key ) . '"' . wc_selected( $key, $selections ) . '>' . esc_html( $val ) . '</option>'; // WPCS: XSS ok.
806 }
807 }
808 ?>
809 </select> <?php echo ( $description ) ? $description : ''; // WPCS: XSS ok. ?> <br /><a class="select_all button" href="#"><?php esc_html_e( 'Select all', 'woocommerce' ); ?></a> <a class="select_none button" href="#"><?php esc_html_e( 'Select none', 'woocommerce' ); ?></a>
810 </td>
811 </tr>
812 <?php
813 break;
814
815 // Days/months/years selector.
816 case 'relative_date_selector':
817 $periods = array(
818 'days' => __( 'Day(s)', 'woocommerce' ),
819 'weeks' => __( 'Week(s)', 'woocommerce' ),
820 'months' => __( 'Month(s)', 'woocommerce' ),
821 'years' => __( 'Year(s)', 'woocommerce' ),
822 );
823 $option_value = wc_parse_relative_date_option( $value['value'] );
824 ?>
825 <tr class="<?php echo esc_attr( $value['row_class'] ); ?>">
826 <th scope="row" class="titledesc">
827 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
828 </th>
829 <td class="forminp">
830 <input
831 name="<?php echo esc_attr( $value['field_name'] ); ?>[number]"
832 id="<?php echo esc_attr( $value['id'] ); ?>"
833 type="number"
834 style="width: 80px;"
835 value="<?php echo esc_attr( $option_value['number'] ); ?>"
836 class="<?php echo esc_attr( $value['class'] ); ?>"
837 placeholder="<?php echo esc_attr( $value['placeholder'] ); ?>"
838 step="1"
839 min="1"
840 <?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
841 />&nbsp;
842 <select name="<?php echo esc_attr( $value['field_name'] ); ?>[unit]" style="width: auto;">
843 <?php
844 foreach ( $periods as $value => $label ) {
845 echo '<option value="' . esc_attr( $value ) . '"' . selected( $option_value['unit'], $value, false ) . '>' . esc_html( $label ) . '</option>';
846 }
847 ?>
848 </select> <?php echo ( $description ) ? $description : ''; // WPCS: XSS ok. ?>
849 </td>
850 </tr>
851 <?php
852 break;
853
854 case 'slotfill_placeholder':
855 ?>
856 <div
857 id="<?php echo esc_attr( $value['id'] ); ?>"
858 class="<?php echo esc_attr( $value['class'] ); ?>"
859 >
860 </div>
861 <?php
862 break;
863 // Default: run an action.
864 default:
865 do_action( 'woocommerce_admin_field_' . $value['type'], $value );
866 break;
867 }
868 }
869 }
870
871 /**
872 * Helper function to get the formatted description and tip HTML for a
873 * given form field. Plugins can call this when implementing their own custom
874 * settings types.
875 *
876 * @param array $value The form field value array.
877 * @return array The description and tip as a 2 element array.
878 */
879 public static function get_field_description( $value ) {
880 $description = '';
881 $tooltip_html = '';
882
883 if ( true === $value['desc_tip'] ) {
884 $tooltip_html = $value['desc'];
885 } elseif ( ! empty( $value['desc_tip'] ) ) {
886 $description = $value['desc'];
887 $tooltip_html = $value['desc_tip'];
888 } elseif ( ! empty( $value['desc'] ) ) {
889 $description = $value['desc'];
890 }
891
892 $desc_at_end = ( isset( $value['desc_at_end'] ) ? $value['desc_at_end'] : false );
893 $error_class = ( ! empty( $value['description_is_error'] ) ) ? 'is-error' : '';
894
895 if ( $description && in_array( $value['type'], array( 'textarea' ), true ) && true !== $desc_at_end ) {
896 $description = '<p class="description ' . $error_class . '" style="margin-top:0;">' . wp_kses_post( $description ) . '</p>';
897 } elseif ( $description && in_array( $value['type'], array( 'radio' ), true ) ) {
898 $description = '<p style="margin-top:0">' . wp_kses_post( $description ) . '</p>';
899 } elseif ( $description && in_array( $value['type'], array( 'checkbox' ), true ) ) {
900 $description = wp_kses_post( $description );
901 } elseif ( $description ) {
902 $description = '<p class="description ' . $error_class . '">' . wp_kses_post( $description ) . '</p>';
903 }
904
905 if ( $tooltip_html && in_array( $value['type'], array( 'checkbox' ), true ) ) {
906 $tooltip_html = '<p class="description ' . $error_class . '">' . $tooltip_html . '</p>';
907 } elseif ( $tooltip_html ) {
908 $tooltip_html = wc_help_tip( $tooltip_html );
909 }
910
911 return array(
912 'description' => $description,
913 'tooltip_html' => $tooltip_html,
914 );
915 }
916
917 /**
918 * Save admin fields.
919 *
920 * Loops through the woocommerce options array and outputs each field.
921 *
922 * @param array $options Options array to output.
923 * @param array $data Optional. Data to use for saving. Defaults to $_POST.
924 * @return bool
925 */
926 public static function save_fields( $options, $data = null ) {
927 if ( is_null( $data ) ) {
928 $data = $_POST; // WPCS: input var okay, CSRF ok.
929 }
930 if ( empty( $data ) ) {
931 return false;
932 }
933
934 // Options to update will be stored here and saved later.
935 $update_options = array();
936 $autoload_options = array();
937
938 // Loop options and get values to save.
939 foreach ( $options as $option ) {
940 if ( ! isset( $option['id'] ) || ! isset( $option['type'] ) || ( isset( $option['is_option'] ) && false === $option['is_option'] ) ) {
941 continue;
942 }
943
944 $option_name = $option['field_name'] ?? $option['id'];
945
946 // Get posted value.
947 if ( strstr( $option_name, '[' ) ) {
948 parse_str( $option_name, $option_name_array );
949 $option_name = current( array_keys( $option_name_array ) );
950 $setting_name = key( $option_name_array[ $option_name ] );
951 $raw_value = isset( $data[ $option_name ][ $setting_name ] ) ? wp_unslash( $data[ $option_name ][ $setting_name ] ) : null;
952 } else {
953 $setting_name = '';
954 $raw_value = isset( $data[ $option_name ] ) ? wp_unslash( $data[ $option_name ] ) : null;
955 }
956
957 // Format the value based on option type.
958 switch ( $option['type'] ) {
959 case 'checkbox':
960 $value = '1' === $raw_value || 'yes' === $raw_value ? 'yes' : 'no';
961 break;
962 case 'textarea':
963 $value = wp_kses_post( trim( $raw_value ) );
964 break;
965 case 'multiselect':
966 case 'multi_select_countries':
967 $value = array_filter( array_map( 'wc_clean', (array) $raw_value ) );
968 break;
969 case 'image_width':
970 $value = array();
971 if ( isset( $raw_value['width'] ) ) {
972 $value['width'] = wc_clean( $raw_value['width'] );
973 $value['height'] = wc_clean( $raw_value['height'] );
974 $value['crop'] = isset( $raw_value['crop'] ) ? 1 : 0;
975 } else {
976 $value['width'] = $option['default']['width'];
977 $value['height'] = $option['default']['height'];
978 $value['crop'] = $option['default']['crop'];
979 }
980 break;
981 case 'select':
982 $allowed_values = empty( $option['options'] ) ? array() : array_map( 'strval', array_keys( $option['options'] ) );
983 if ( empty( $option['default'] ) && empty( $allowed_values ) ) {
984 $value = null;
985 break;
986 }
987 $default = ( empty( $option['default'] ) ? $allowed_values[0] : $option['default'] );
988 $value = in_array( $raw_value, $allowed_values, true ) ? $raw_value : $default;
989 break;
990 case 'relative_date_selector':
991 $value = wc_parse_relative_date_option( $raw_value );
992 break;
993 case 'password':
994 // Non-string or absent → null so the option is skipped, not overwritten.
995 // Only trim — no wp_strip_all_tags() or wc_clean() which would corrupt
996 // passwords containing '<' or percent-like sequences.
997 // $raw_value is already wp_unslash()ed upstream, so no stripslashes() needed.
998 // Matches WC_Settings_API::validate_password_field() behavior.
999 $value = is_string( $raw_value ) ? trim( $raw_value ) : null;
1000 break;
1001 default:
1002 $value = wc_clean( $raw_value );
1003 break;
1004 }
1005
1006 /**
1007 * Fire an action when a certain 'type' of field is being saved.
1008 *
1009 * @deprecated 2.4.0 - doesn't allow manipulation of values!
1010 */
1011 if ( has_action( 'woocommerce_update_option_' . sanitize_title( $option['type'] ) ) ) {
1012 wc_deprecated_function( 'The woocommerce_update_option_X action', '2.4.0', 'woocommerce_admin_settings_sanitize_option filter' );
1013 do_action( 'woocommerce_update_option_' . sanitize_title( $option['type'] ), $option );
1014 continue;
1015 }
1016
1017 /**
1018 * Sanitize the value of an option.
1019 *
1020 * @since 2.4.0
1021 */
1022 $value = apply_filters( 'woocommerce_admin_settings_sanitize_option', $value, $option, $raw_value );
1023
1024 /**
1025 * Sanitize the value of an option by option name.
1026 *
1027 * @since 2.4.0
1028 */
1029 $value = apply_filters( "woocommerce_admin_settings_sanitize_option_$option_name", $value, $option, $raw_value );
1030
1031 if ( is_null( $value ) ) {
1032 continue;
1033 }
1034
1035 // Check if option is an array and handle that differently to single values.
1036 if ( $option_name && $setting_name ) {
1037 if ( ! isset( $update_options[ $option_name ] ) ) {
1038 $update_options[ $option_name ] = get_option( $option_name, array() );
1039 }
1040 if ( ! is_array( $update_options[ $option_name ] ) ) {
1041 $update_options[ $option_name ] = array();
1042 }
1043 $update_options[ $option_name ][ $setting_name ] = $value;
1044 } else {
1045 $update_options[ $option_name ] = $value;
1046 }
1047
1048 $autoload_options[ $option_name ] = isset( $option['autoload'] ) ? (bool) $option['autoload'] : true;
1049
1050 /**
1051 * Fire an action before saved.
1052 *
1053 * @deprecated 2.4.0 - doesn't allow manipulation of values!
1054 */
1055 do_action( 'woocommerce_update_option', $option );
1056 }
1057
1058 // Save all options in our array.
1059 foreach ( $update_options as $name => $value ) {
1060 update_option( $name, $value, $autoload_options[ $name ] ? 'yes' : 'no' );
1061 }
1062
1063 return true;
1064 }
1065
1066 /**
1067 * Checks which method we're using to serve downloads.
1068 *
1069 * If using force or x-sendfile, this ensures the .htaccess is in place.
1070 */
1071 public static function check_download_folder_protection() {
1072 $upload_dir = wp_get_upload_dir();
1073 $downloads_path = $upload_dir['basedir'] . '/woocommerce_uploads';
1074 $download_method = get_option( 'woocommerce_file_download_method' );
1075 $file_path = $downloads_path . '/.htaccess';
1076 $file_content = 'redirect' === $download_method ? 'Options -Indexes' : 'deny from all';
1077 $create = false;
1078
1079 if ( wp_mkdir_p( $downloads_path ) && ! file_exists( $file_path ) ) {
1080 $create = true;
1081 } else {
1082 $current_content = @file_get_contents( $file_path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
1083
1084 if ( $current_content !== $file_content ) {
1085 unlink( $file_path );
1086 $create = true;
1087 }
1088 }
1089
1090 if ( $create ) {
1091 $file_handle = @fopen( $file_path, 'wb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fopen
1092 if ( $file_handle ) {
1093 fwrite( $file_handle, $file_content ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fwrite
1094 fclose( $file_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
1095 }
1096 }
1097 }
1098 }
1099
1100 endif;
1101