Açıklama Yok

class-wc-admin-settings.php 34KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  1. <?php
  2. /**
  3. * WooCommerce Admin Settings Class
  4. *
  5. * @package WooCommerce\Admin
  6. * @version 3.4.0
  7. */
  8. use Automattic\Jetpack\Constants;
  9. if ( ! defined( 'ABSPATH' ) ) {
  10. exit;
  11. }
  12. if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
  13. /**
  14. * WC_Admin_Settings Class.
  15. */
  16. class WC_Admin_Settings {
  17. /**
  18. * Setting pages.
  19. *
  20. * @var array
  21. */
  22. private static $settings = array();
  23. /**
  24. * Error messages.
  25. *
  26. * @var array
  27. */
  28. private static $errors = array();
  29. /**
  30. * Update messages.
  31. *
  32. * @var array
  33. */
  34. private static $messages = array();
  35. /**
  36. * Include the settings page classes.
  37. */
  38. public static function get_settings_pages() {
  39. if ( empty( self::$settings ) ) {
  40. $settings = array();
  41. include_once dirname( __FILE__ ) . '/settings/class-wc-settings-page.php';
  42. $settings[] = include __DIR__ . '/settings/class-wc-settings-general.php';
  43. $settings[] = include __DIR__ . '/settings/class-wc-settings-products.php';
  44. $settings[] = include __DIR__ . '/settings/class-wc-settings-tax.php';
  45. $settings[] = include __DIR__ . '/settings/class-wc-settings-shipping.php';
  46. $settings[] = include __DIR__ . '/settings/class-wc-settings-payment-gateways.php';
  47. $settings[] = include __DIR__ . '/settings/class-wc-settings-accounts.php';
  48. $settings[] = include __DIR__ . '/settings/class-wc-settings-emails.php';
  49. $settings[] = include __DIR__ . '/settings/class-wc-settings-integrations.php';
  50. $settings[] = include __DIR__ . '/settings/class-wc-settings-advanced.php';
  51. self::$settings = apply_filters( 'woocommerce_get_settings_pages', $settings );
  52. }
  53. return self::$settings;
  54. }
  55. /**
  56. * Save the settings.
  57. */
  58. public static function save() {
  59. global $current_tab;
  60. check_admin_referer( 'woocommerce-settings' );
  61. // Trigger actions.
  62. do_action( 'woocommerce_settings_save_' . $current_tab );
  63. do_action( 'woocommerce_update_options_' . $current_tab );
  64. do_action( 'woocommerce_update_options' );
  65. self::add_message( __( 'Your settings have been saved.', 'woocommerce' ) );
  66. self::check_download_folder_protection();
  67. // Clear any unwanted data and flush rules.
  68. update_option( 'woocommerce_queue_flush_rewrite_rules', 'yes' );
  69. WC()->query->init_query_vars();
  70. WC()->query->add_endpoints();
  71. do_action( 'woocommerce_settings_saved' );
  72. }
  73. /**
  74. * Add a message.
  75. *
  76. * @param string $text Message.
  77. */
  78. public static function add_message( $text ) {
  79. self::$messages[] = $text;
  80. }
  81. /**
  82. * Add an error.
  83. *
  84. * @param string $text Message.
  85. */
  86. public static function add_error( $text ) {
  87. self::$errors[] = $text;
  88. }
  89. /**
  90. * Output messages + errors.
  91. */
  92. public static function show_messages() {
  93. if ( count( self::$errors ) > 0 ) {
  94. foreach ( self::$errors as $error ) {
  95. echo '<div id="message" class="error inline"><p><strong>' . esc_html( $error ) . '</strong></p></div>';
  96. }
  97. } elseif ( count( self::$messages ) > 0 ) {
  98. foreach ( self::$messages as $message ) {
  99. echo '<div id="message" class="updated inline"><p><strong>' . esc_html( $message ) . '</strong></p></div>';
  100. }
  101. }
  102. }
  103. /**
  104. * Settings page.
  105. *
  106. * Handles the display of the main woocommerce settings page in admin.
  107. */
  108. public static function output() {
  109. global $current_section, $current_tab;
  110. $suffix = Constants::is_true( 'SCRIPT_DEBUG' ) ? '' : '.min';
  111. do_action( 'woocommerce_settings_start' );
  112. 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 );
  113. wp_localize_script(
  114. 'woocommerce_settings',
  115. 'woocommerce_settings_params',
  116. array(
  117. 'i18n_nav_warning' => __( 'The changes you made will be lost if you navigate away from this page.', 'woocommerce' ),
  118. 'i18n_moved_up' => __( 'Item moved up', 'woocommerce' ),
  119. 'i18n_moved_down' => __( 'Item moved down', 'woocommerce' ),
  120. 'i18n_no_specific_countries_selected' => __( 'Selecting no country / region to sell to prevents from completing the checkout. Continue anyway?', 'woocommerce' ),
  121. )
  122. );
  123. // Get tabs for the settings page.
  124. $tabs = apply_filters( 'woocommerce_settings_tabs_array', array() );
  125. include dirname( __FILE__ ) . '/views/html-admin-settings.php';
  126. }
  127. /**
  128. * Get a setting from the settings API.
  129. *
  130. * @param string $option_name Option name.
  131. * @param mixed $default Default value.
  132. * @return mixed
  133. */
  134. public static function get_option( $option_name, $default = '' ) {
  135. if ( ! $option_name ) {
  136. return $default;
  137. }
  138. // Array value.
  139. if ( strstr( $option_name, '[' ) ) {
  140. parse_str( $option_name, $option_array );
  141. // Option name is first key.
  142. $option_name = current( array_keys( $option_array ) );
  143. // Get value.
  144. $option_values = get_option( $option_name, '' );
  145. $key = key( $option_array[ $option_name ] );
  146. if ( isset( $option_values[ $key ] ) ) {
  147. $option_value = $option_values[ $key ];
  148. } else {
  149. $option_value = null;
  150. }
  151. } else {
  152. // Single value.
  153. $option_value = get_option( $option_name, null );
  154. }
  155. if ( is_array( $option_value ) ) {
  156. $option_value = wp_unslash( $option_value );
  157. } elseif ( ! is_null( $option_value ) ) {
  158. $option_value = stripslashes( $option_value );
  159. }
  160. return ( null === $option_value ) ? $default : $option_value;
  161. }
  162. /**
  163. * Output admin fields.
  164. *
  165. * Loops through the woocommerce options array and outputs each field.
  166. *
  167. * @param array[] $options Opens array to output.
  168. */
  169. public static function output_fields( $options ) {
  170. foreach ( $options as $value ) {
  171. if ( ! isset( $value['type'] ) ) {
  172. continue;
  173. }
  174. if ( ! isset( $value['id'] ) ) {
  175. $value['id'] = '';
  176. }
  177. if ( ! isset( $value['title'] ) ) {
  178. $value['title'] = isset( $value['name'] ) ? $value['name'] : '';
  179. }
  180. if ( ! isset( $value['class'] ) ) {
  181. $value['class'] = '';
  182. }
  183. if ( ! isset( $value['css'] ) ) {
  184. $value['css'] = '';
  185. }
  186. if ( ! isset( $value['default'] ) ) {
  187. $value['default'] = '';
  188. }
  189. if ( ! isset( $value['desc'] ) ) {
  190. $value['desc'] = '';
  191. }
  192. if ( ! isset( $value['desc_tip'] ) ) {
  193. $value['desc_tip'] = false;
  194. }
  195. if ( ! isset( $value['placeholder'] ) ) {
  196. $value['placeholder'] = '';
  197. }
  198. if ( ! isset( $value['suffix'] ) ) {
  199. $value['suffix'] = '';
  200. }
  201. if ( ! isset( $value['value'] ) ) {
  202. $value['value'] = self::get_option( $value['id'], $value['default'] );
  203. }
  204. // Custom attribute handling.
  205. $custom_attributes = array();
  206. if ( ! empty( $value['custom_attributes'] ) && is_array( $value['custom_attributes'] ) ) {
  207. foreach ( $value['custom_attributes'] as $attribute => $attribute_value ) {
  208. $custom_attributes[] = esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '"';
  209. }
  210. }
  211. // Description handling.
  212. $field_description = self::get_field_description( $value );
  213. $description = $field_description['description'];
  214. $tooltip_html = $field_description['tooltip_html'];
  215. // Switch based on type.
  216. switch ( $value['type'] ) {
  217. // Section Titles.
  218. case 'title':
  219. if ( ! empty( $value['title'] ) ) {
  220. echo '<h2>' . esc_html( $value['title'] ) . '</h2>';
  221. }
  222. if ( ! empty( $value['desc'] ) ) {
  223. echo '<div id="' . esc_attr( sanitize_title( $value['id'] ) ) . '-description">';
  224. echo wp_kses_post( wpautop( wptexturize( $value['desc'] ) ) );
  225. echo '</div>';
  226. }
  227. echo '<table class="form-table">' . "\n\n";
  228. if ( ! empty( $value['id'] ) ) {
  229. do_action( 'woocommerce_settings_' . sanitize_title( $value['id'] ) );
  230. }
  231. break;
  232. // Section Ends.
  233. case 'sectionend':
  234. if ( ! empty( $value['id'] ) ) {
  235. do_action( 'woocommerce_settings_' . sanitize_title( $value['id'] ) . '_end' );
  236. }
  237. echo '</table>';
  238. if ( ! empty( $value['id'] ) ) {
  239. do_action( 'woocommerce_settings_' . sanitize_title( $value['id'] ) . '_after' );
  240. }
  241. break;
  242. // Standard text inputs and subtypes like 'number'.
  243. case 'text':
  244. case 'password':
  245. case 'datetime':
  246. case 'datetime-local':
  247. case 'date':
  248. case 'month':
  249. case 'time':
  250. case 'week':
  251. case 'number':
  252. case 'email':
  253. case 'url':
  254. case 'tel':
  255. $option_value = $value['value'];
  256. ?><tr valign="top">
  257. <th scope="row" class="titledesc">
  258. <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
  259. </th>
  260. <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ); ?>">
  261. <input
  262. name="<?php echo esc_attr( $value['id'] ); ?>"
  263. id="<?php echo esc_attr( $value['id'] ); ?>"
  264. type="<?php echo esc_attr( $value['type'] ); ?>"
  265. style="<?php echo esc_attr( $value['css'] ); ?>"
  266. value="<?php echo esc_attr( $option_value ); ?>"
  267. class="<?php echo esc_attr( $value['class'] ); ?>"
  268. placeholder="<?php echo esc_attr( $value['placeholder'] ); ?>"
  269. <?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
  270. /><?php echo esc_html( $value['suffix'] ); ?> <?php echo $description; // WPCS: XSS ok. ?>
  271. </td>
  272. </tr>
  273. <?php
  274. break;
  275. // Color picker.
  276. case 'color':
  277. $option_value = $value['value'];
  278. ?>
  279. <tr valign="top">
  280. <th scope="row" class="titledesc">
  281. <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
  282. </th>
  283. <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ); ?>">&lrm;
  284. <span class="colorpickpreview" style="background: <?php echo esc_attr( $option_value ); ?>">&nbsp;</span>
  285. <input
  286. name="<?php echo esc_attr( $value['id'] ); ?>"
  287. id="<?php echo esc_attr( $value['id'] ); ?>"
  288. type="text"
  289. dir="ltr"
  290. style="<?php echo esc_attr( $value['css'] ); ?>"
  291. value="<?php echo esc_attr( $option_value ); ?>"
  292. class="<?php echo esc_attr( $value['class'] ); ?>colorpick"
  293. placeholder="<?php echo esc_attr( $value['placeholder'] ); ?>"
  294. <?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
  295. />&lrm; <?php echo $description; // WPCS: XSS ok. ?>
  296. <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>
  297. </td>
  298. </tr>
  299. <?php
  300. break;
  301. // Textarea.
  302. case 'textarea':
  303. $option_value = $value['value'];
  304. ?>
  305. <tr valign="top">
  306. <th scope="row" class="titledesc">
  307. <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
  308. </th>
  309. <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ); ?>">
  310. <?php echo $description; // WPCS: XSS ok. ?>
  311. <textarea
  312. name="<?php echo esc_attr( $value['id'] ); ?>"
  313. id="<?php echo esc_attr( $value['id'] ); ?>"
  314. style="<?php echo esc_attr( $value['css'] ); ?>"
  315. class="<?php echo esc_attr( $value['class'] ); ?>"
  316. placeholder="<?php echo esc_attr( $value['placeholder'] ); ?>"
  317. <?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
  318. ><?php echo esc_textarea( $option_value ); // WPCS: XSS ok. ?></textarea>
  319. </td>
  320. </tr>
  321. <?php
  322. break;
  323. // Select boxes.
  324. case 'select':
  325. case 'multiselect':
  326. $option_value = $value['value'];
  327. ?>
  328. <tr valign="top">
  329. <th scope="row" class="titledesc">
  330. <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
  331. </th>
  332. <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ); ?>">
  333. <select
  334. name="<?php echo esc_attr( $value['id'] ); ?><?php echo ( 'multiselect' === $value['type'] ) ? '[]' : ''; ?>"
  335. id="<?php echo esc_attr( $value['id'] ); ?>"
  336. style="<?php echo esc_attr( $value['css'] ); ?>"
  337. class="<?php echo esc_attr( $value['class'] ); ?>"
  338. <?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
  339. <?php echo 'multiselect' === $value['type'] ? 'multiple="multiple"' : ''; ?>
  340. >
  341. <?php
  342. foreach ( $value['options'] as $key => $val ) {
  343. ?>
  344. <option value="<?php echo esc_attr( $key ); ?>"
  345. <?php
  346. if ( is_array( $option_value ) ) {
  347. selected( in_array( (string) $key, $option_value, true ), true );
  348. } else {
  349. selected( $option_value, (string) $key );
  350. }
  351. ?>
  352. ><?php echo esc_html( $val ); ?></option>
  353. <?php
  354. }
  355. ?>
  356. </select> <?php echo $description; // WPCS: XSS ok. ?>
  357. </td>
  358. </tr>
  359. <?php
  360. break;
  361. // Radio inputs.
  362. case 'radio':
  363. $option_value = $value['value'];
  364. ?>
  365. <tr valign="top">
  366. <th scope="row" class="titledesc">
  367. <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
  368. </th>
  369. <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ); ?>">
  370. <fieldset>
  371. <?php echo $description; // WPCS: XSS ok. ?>
  372. <ul>
  373. <?php
  374. foreach ( $value['options'] as $key => $val ) {
  375. ?>
  376. <li>
  377. <label><input
  378. name="<?php echo esc_attr( $value['id'] ); ?>"
  379. value="<?php echo esc_attr( $key ); ?>"
  380. type="radio"
  381. style="<?php echo esc_attr( $value['css'] ); ?>"
  382. class="<?php echo esc_attr( $value['class'] ); ?>"
  383. <?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
  384. <?php checked( $key, $option_value ); ?>
  385. /> <?php echo esc_html( $val ); ?></label>
  386. </li>
  387. <?php
  388. }
  389. ?>
  390. </ul>
  391. </fieldset>
  392. </td>
  393. </tr>
  394. <?php
  395. break;
  396. // Checkbox input.
  397. case 'checkbox':
  398. $option_value = $value['value'];
  399. $visibility_class = array();
  400. if ( ! isset( $value['hide_if_checked'] ) ) {
  401. $value['hide_if_checked'] = false;
  402. }
  403. if ( ! isset( $value['show_if_checked'] ) ) {
  404. $value['show_if_checked'] = false;
  405. }
  406. if ( 'yes' === $value['hide_if_checked'] || 'yes' === $value['show_if_checked'] ) {
  407. $visibility_class[] = 'hidden_option';
  408. }
  409. if ( 'option' === $value['hide_if_checked'] ) {
  410. $visibility_class[] = 'hide_options_if_checked';
  411. }
  412. if ( 'option' === $value['show_if_checked'] ) {
  413. $visibility_class[] = 'show_options_if_checked';
  414. }
  415. if ( ! isset( $value['checkboxgroup'] ) || 'start' === $value['checkboxgroup'] ) {
  416. ?>
  417. <tr valign="top" class="<?php echo esc_attr( implode( ' ', $visibility_class ) ); ?>">
  418. <th scope="row" class="titledesc"><?php echo esc_html( $value['title'] ); ?></th>
  419. <td class="forminp forminp-checkbox">
  420. <fieldset>
  421. <?php
  422. } else {
  423. ?>
  424. <fieldset class="<?php echo esc_attr( implode( ' ', $visibility_class ) ); ?>">
  425. <?php
  426. }
  427. if ( ! empty( $value['title'] ) ) {
  428. ?>
  429. <legend class="screen-reader-text"><span><?php echo esc_html( $value['title'] ); ?></span></legend>
  430. <?php
  431. }
  432. ?>
  433. <label for="<?php echo esc_attr( $value['id'] ); ?>">
  434. <input
  435. name="<?php echo esc_attr( $value['id'] ); ?>"
  436. id="<?php echo esc_attr( $value['id'] ); ?>"
  437. type="checkbox"
  438. class="<?php echo esc_attr( isset( $value['class'] ) ? $value['class'] : '' ); ?>"
  439. value="1"
  440. <?php checked( $option_value, 'yes' ); ?>
  441. <?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
  442. /> <?php echo $description; // WPCS: XSS ok. ?>
  443. </label> <?php echo $tooltip_html; // WPCS: XSS ok. ?>
  444. <?php
  445. if ( ! isset( $value['checkboxgroup'] ) || 'end' === $value['checkboxgroup'] ) {
  446. ?>
  447. </fieldset>
  448. </td>
  449. </tr>
  450. <?php
  451. } else {
  452. ?>
  453. </fieldset>
  454. <?php
  455. }
  456. break;
  457. // Image width settings. @todo deprecate and remove in 4.0. No longer needed by core.
  458. case 'image_width':
  459. $image_size = str_replace( '_image_size', '', $value['id'] );
  460. $size = wc_get_image_size( $image_size );
  461. $width = isset( $size['width'] ) ? $size['width'] : $value['default']['width'];
  462. $height = isset( $size['height'] ) ? $size['height'] : $value['default']['height'];
  463. $crop = isset( $size['crop'] ) ? $size['crop'] : $value['default']['crop'];
  464. $disabled_attr = '';
  465. $disabled_message = '';
  466. if ( has_filter( 'woocommerce_get_image_size_' . $image_size ) ) {
  467. $disabled_attr = 'disabled="disabled"';
  468. $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>';
  469. }
  470. ?>
  471. <tr valign="top">
  472. <th scope="row" class="titledesc">
  473. <label><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html . $disabled_message; // WPCS: XSS ok. ?></label>
  474. </th>
  475. <td class="forminp image_width_settings">
  476. <input name="<?php echo esc_attr( $value['id'] ); ?>[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
  477. <label><input name="<?php echo esc_attr( $value['id'] ); ?>[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>
  478. </td>
  479. </tr>
  480. <?php
  481. break;
  482. // Single page selects.
  483. case 'single_select_page':
  484. $args = array(
  485. 'name' => $value['id'],
  486. 'id' => $value['id'],
  487. 'sort_column' => 'menu_order',
  488. 'sort_order' => 'ASC',
  489. 'show_option_none' => ' ',
  490. 'class' => $value['class'],
  491. 'echo' => false,
  492. 'selected' => absint( $value['value'] ),
  493. 'post_status' => 'publish,private,draft',
  494. );
  495. if ( isset( $value['args'] ) ) {
  496. $args = wp_parse_args( $value['args'], $args );
  497. }
  498. ?>
  499. <tr valign="top" class="single_select_page">
  500. <th scope="row" class="titledesc">
  501. <label><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
  502. </th>
  503. <td class="forminp">
  504. <?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. ?>
  505. </td>
  506. </tr>
  507. <?php
  508. break;
  509. case 'single_select_page_with_search':
  510. $option_value = $value['value'];
  511. $page = get_post( $option_value );
  512. if ( ! is_null( $page ) ) {
  513. $page = get_post( $option_value );
  514. $option_display_name = sprintf(
  515. /* translators: 1: page name 2: page ID */
  516. __( '%1$s (ID: %2$s)', 'woocommerce' ),
  517. $page->post_title,
  518. $option_value
  519. );
  520. }
  521. ?>
  522. <tr valign="top" class="single_select_page">
  523. <th scope="row" class="titledesc">
  524. <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>
  525. </th>
  526. <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ); ?>">
  527. <select
  528. name="<?php echo esc_attr( $value['id'] ); ?>"
  529. id="<?php echo esc_attr( $value['id'] ); ?>"
  530. style="<?php echo esc_attr( $value['css'] ); ?>"
  531. class="<?php echo esc_attr( $value['class'] ); ?>"
  532. <?php echo implode( ' ', $custom_attributes ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
  533. data-placeholder="<?php esc_attr_e( 'Search for a page&hellip;', 'woocommerce' ); ?>"
  534. data-allow_clear="true"
  535. data-exclude="<?php echo wc_esc_json( wp_json_encode( $value['args']['exclude'] ) ); ?>"
  536. >
  537. <option value=""></option>
  538. <?php if ( ! is_null( $page ) ) { ?>
  539. <option value="<?php echo esc_attr( $option_value ); ?>" selected="selected">
  540. <?php echo wp_strip_all_tags( $option_display_name ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
  541. </option>
  542. <?php } ?>
  543. </select> <?php echo $description; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
  544. </td>
  545. </tr>
  546. <?php
  547. break;
  548. // Single country selects.
  549. case 'single_select_country':
  550. $country_setting = (string) $value['value'];
  551. if ( strstr( $country_setting, ':' ) ) {
  552. $country_setting = explode( ':', $country_setting );
  553. $country = current( $country_setting );
  554. $state = end( $country_setting );
  555. } else {
  556. $country = $country_setting;
  557. $state = '*';
  558. }
  559. ?>
  560. <tr valign="top">
  561. <th scope="row" class="titledesc">
  562. <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
  563. </th>
  564. <td class="forminp"><select name="<?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">
  565. <?php WC()->countries->country_dropdown_options( $country, $state ); ?>
  566. </select> <?php echo $description; // WPCS: XSS ok. ?>
  567. </td>
  568. </tr>
  569. <?php
  570. break;
  571. // Country multiselects.
  572. case 'multi_select_countries':
  573. $selections = (array) $value['value'];
  574. if ( ! empty( $value['options'] ) ) {
  575. $countries = $value['options'];
  576. } else {
  577. $countries = WC()->countries->countries;
  578. }
  579. asort( $countries );
  580. ?>
  581. <tr valign="top">
  582. <th scope="row" class="titledesc">
  583. <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
  584. </th>
  585. <td class="forminp">
  586. <select multiple="multiple" name="<?php echo esc_attr( $value['id'] ); ?>[]" style="width:350px" data-placeholder="<?php esc_attr_e( 'Choose countries / regions&hellip;', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Country / Region', 'woocommerce' ); ?>" class="wc-enhanced-select">
  587. <?php
  588. if ( ! empty( $countries ) ) {
  589. foreach ( $countries as $key => $val ) {
  590. echo '<option value="' . esc_attr( $key ) . '"' . wc_selected( $key, $selections ) . '>' . esc_html( $val ) . '</option>'; // WPCS: XSS ok.
  591. }
  592. }
  593. ?>
  594. </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>
  595. </td>
  596. </tr>
  597. <?php
  598. break;
  599. // Days/months/years selector.
  600. case 'relative_date_selector':
  601. $periods = array(
  602. 'days' => __( 'Day(s)', 'woocommerce' ),
  603. 'weeks' => __( 'Week(s)', 'woocommerce' ),
  604. 'months' => __( 'Month(s)', 'woocommerce' ),
  605. 'years' => __( 'Year(s)', 'woocommerce' ),
  606. );
  607. $option_value = wc_parse_relative_date_option( $value['value'] );
  608. ?>
  609. <tr valign="top">
  610. <th scope="row" class="titledesc">
  611. <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> <?php echo $tooltip_html; // WPCS: XSS ok. ?></label>
  612. </th>
  613. <td class="forminp">
  614. <input
  615. name="<?php echo esc_attr( $value['id'] ); ?>[number]"
  616. id="<?php echo esc_attr( $value['id'] ); ?>"
  617. type="number"
  618. style="width: 80px;"
  619. value="<?php echo esc_attr( $option_value['number'] ); ?>"
  620. class="<?php echo esc_attr( $value['class'] ); ?>"
  621. placeholder="<?php echo esc_attr( $value['placeholder'] ); ?>"
  622. step="1"
  623. min="1"
  624. <?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
  625. />&nbsp;
  626. <select name="<?php echo esc_attr( $value['id'] ); ?>[unit]" style="width: auto;">
  627. <?php
  628. foreach ( $periods as $value => $label ) {
  629. echo '<option value="' . esc_attr( $value ) . '"' . selected( $option_value['unit'], $value, false ) . '>' . esc_html( $label ) . '</option>';
  630. }
  631. ?>
  632. </select> <?php echo ( $description ) ? $description : ''; // WPCS: XSS ok. ?>
  633. </td>
  634. </tr>
  635. <?php
  636. break;
  637. // Default: run an action.
  638. default:
  639. do_action( 'woocommerce_admin_field_' . $value['type'], $value );
  640. break;
  641. }
  642. }
  643. }
  644. /**
  645. * Helper function to get the formatted description and tip HTML for a
  646. * given form field. Plugins can call this when implementing their own custom
  647. * settings types.
  648. *
  649. * @param array $value The form field value array.
  650. * @return array The description and tip as a 2 element array.
  651. */
  652. public static function get_field_description( $value ) {
  653. $description = '';
  654. $tooltip_html = '';
  655. if ( true === $value['desc_tip'] ) {
  656. $tooltip_html = $value['desc'];
  657. } elseif ( ! empty( $value['desc_tip'] ) ) {
  658. $description = $value['desc'];
  659. $tooltip_html = $value['desc_tip'];
  660. } elseif ( ! empty( $value['desc'] ) ) {
  661. $description = $value['desc'];
  662. }
  663. if ( $description && in_array( $value['type'], array( 'textarea', 'radio' ), true ) ) {
  664. $description = '<p style="margin-top:0">' . wp_kses_post( $description ) . '</p>';
  665. } elseif ( $description && in_array( $value['type'], array( 'checkbox' ), true ) ) {
  666. $description = wp_kses_post( $description );
  667. } elseif ( $description ) {
  668. $description = '<p class="description">' . wp_kses_post( $description ) . '</p>';
  669. }
  670. if ( $tooltip_html && in_array( $value['type'], array( 'checkbox' ), true ) ) {
  671. $tooltip_html = '<p class="description">' . $tooltip_html . '</p>';
  672. } elseif ( $tooltip_html ) {
  673. $tooltip_html = wc_help_tip( $tooltip_html );
  674. }
  675. return array(
  676. 'description' => $description,
  677. 'tooltip_html' => $tooltip_html,
  678. );
  679. }
  680. /**
  681. * Save admin fields.
  682. *
  683. * Loops through the woocommerce options array and outputs each field.
  684. *
  685. * @param array $options Options array to output.
  686. * @param array $data Optional. Data to use for saving. Defaults to $_POST.
  687. * @return bool
  688. */
  689. public static function save_fields( $options, $data = null ) {
  690. if ( is_null( $data ) ) {
  691. $data = $_POST; // WPCS: input var okay, CSRF ok.
  692. }
  693. if ( empty( $data ) ) {
  694. return false;
  695. }
  696. // Options to update will be stored here and saved later.
  697. $update_options = array();
  698. $autoload_options = array();
  699. // Loop options and get values to save.
  700. foreach ( $options as $option ) {
  701. if ( ! isset( $option['id'] ) || ! isset( $option['type'] ) || ( isset( $option['is_option'] ) && false === $option['is_option'] ) ) {
  702. continue;
  703. }
  704. // Get posted value.
  705. if ( strstr( $option['id'], '[' ) ) {
  706. parse_str( $option['id'], $option_name_array );
  707. $option_name = current( array_keys( $option_name_array ) );
  708. $setting_name = key( $option_name_array[ $option_name ] );
  709. $raw_value = isset( $data[ $option_name ][ $setting_name ] ) ? wp_unslash( $data[ $option_name ][ $setting_name ] ) : null;
  710. } else {
  711. $option_name = $option['id'];
  712. $setting_name = '';
  713. $raw_value = isset( $data[ $option['id'] ] ) ? wp_unslash( $data[ $option['id'] ] ) : null;
  714. }
  715. // Format the value based on option type.
  716. switch ( $option['type'] ) {
  717. case 'checkbox':
  718. $value = '1' === $raw_value || 'yes' === $raw_value ? 'yes' : 'no';
  719. break;
  720. case 'textarea':
  721. $value = wp_kses_post( trim( $raw_value ) );
  722. break;
  723. case 'multiselect':
  724. case 'multi_select_countries':
  725. $value = array_filter( array_map( 'wc_clean', (array) $raw_value ) );
  726. break;
  727. case 'image_width':
  728. $value = array();
  729. if ( isset( $raw_value['width'] ) ) {
  730. $value['width'] = wc_clean( $raw_value['width'] );
  731. $value['height'] = wc_clean( $raw_value['height'] );
  732. $value['crop'] = isset( $raw_value['crop'] ) ? 1 : 0;
  733. } else {
  734. $value['width'] = $option['default']['width'];
  735. $value['height'] = $option['default']['height'];
  736. $value['crop'] = $option['default']['crop'];
  737. }
  738. break;
  739. case 'select':
  740. $allowed_values = empty( $option['options'] ) ? array() : array_map( 'strval', array_keys( $option['options'] ) );
  741. if ( empty( $option['default'] ) && empty( $allowed_values ) ) {
  742. $value = null;
  743. break;
  744. }
  745. $default = ( empty( $option['default'] ) ? $allowed_values[0] : $option['default'] );
  746. $value = in_array( $raw_value, $allowed_values, true ) ? $raw_value : $default;
  747. break;
  748. case 'relative_date_selector':
  749. $value = wc_parse_relative_date_option( $raw_value );
  750. break;
  751. default:
  752. $value = wc_clean( $raw_value );
  753. break;
  754. }
  755. /**
  756. * Fire an action when a certain 'type' of field is being saved.
  757. *
  758. * @deprecated 2.4.0 - doesn't allow manipulation of values!
  759. */
  760. if ( has_action( 'woocommerce_update_option_' . sanitize_title( $option['type'] ) ) ) {
  761. wc_deprecated_function( 'The woocommerce_update_option_X action', '2.4.0', 'woocommerce_admin_settings_sanitize_option filter' );
  762. do_action( 'woocommerce_update_option_' . sanitize_title( $option['type'] ), $option );
  763. continue;
  764. }
  765. /**
  766. * Sanitize the value of an option.
  767. *
  768. * @since 2.4.0
  769. */
  770. $value = apply_filters( 'woocommerce_admin_settings_sanitize_option', $value, $option, $raw_value );
  771. /**
  772. * Sanitize the value of an option by option name.
  773. *
  774. * @since 2.4.0
  775. */
  776. $value = apply_filters( "woocommerce_admin_settings_sanitize_option_$option_name", $value, $option, $raw_value );
  777. if ( is_null( $value ) ) {
  778. continue;
  779. }
  780. // Check if option is an array and handle that differently to single values.
  781. if ( $option_name && $setting_name ) {
  782. if ( ! isset( $update_options[ $option_name ] ) ) {
  783. $update_options[ $option_name ] = get_option( $option_name, array() );
  784. }
  785. if ( ! is_array( $update_options[ $option_name ] ) ) {
  786. $update_options[ $option_name ] = array();
  787. }
  788. $update_options[ $option_name ][ $setting_name ] = $value;
  789. } else {
  790. $update_options[ $option_name ] = $value;
  791. }
  792. $autoload_options[ $option_name ] = isset( $option['autoload'] ) ? (bool) $option['autoload'] : true;
  793. /**
  794. * Fire an action before saved.
  795. *
  796. * @deprecated 2.4.0 - doesn't allow manipulation of values!
  797. */
  798. do_action( 'woocommerce_update_option', $option );
  799. }
  800. // Save all options in our array.
  801. foreach ( $update_options as $name => $value ) {
  802. update_option( $name, $value, $autoload_options[ $name ] ? 'yes' : 'no' );
  803. }
  804. return true;
  805. }
  806. /**
  807. * Checks which method we're using to serve downloads.
  808. *
  809. * If using force or x-sendfile, this ensures the .htaccess is in place.
  810. */
  811. public static function check_download_folder_protection() {
  812. $upload_dir = wp_get_upload_dir();
  813. $downloads_path = $upload_dir['basedir'] . '/woocommerce_uploads';
  814. $download_method = get_option( 'woocommerce_file_download_method' );
  815. $file_path = $downloads_path . '/.htaccess';
  816. $file_content = 'redirect' === $download_method ? 'Options -Indexes' : 'deny from all';
  817. $create = false;
  818. if ( wp_mkdir_p( $downloads_path ) && ! file_exists( $file_path ) ) {
  819. $create = true;
  820. } else {
  821. $current_content = @file_get_contents( $file_path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
  822. if ( $current_content !== $file_content ) {
  823. unlink( $file_path );
  824. $create = true;
  825. }
  826. }
  827. if ( $create ) {
  828. $file_handle = @fopen( $file_path, 'wb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fopen
  829. if ( $file_handle ) {
  830. fwrite( $file_handle, $file_content ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fwrite
  831. fclose( $file_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
  832. }
  833. }
  834. }
  835. }
  836. endif;