class-order-fields-manager.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. <?php
  2. // Manages custom order fields rendered in the "Print Information" panel.
  3. defined('ABSPATH') || exit;
  4. class Order_Fields_Manager {
  5. /**
  6. * Meta key → translatable label for fields rendered as `datetime-local`.
  7. * The render and save loops both walk this map.
  8. */
  9. private static function datetime_fields() {
  10. return array(
  11. 'to_print_date' => __('Prepare To Print Date', 'studiou-wc-ord-print-statuses'),
  12. 'in_print_date' => __('In Print Date', 'studiou-wc-ord-print-statuses'),
  13. 'external_ref_ord_date' => __('External Order Date', 'studiou-wc-ord-print-statuses'),
  14. 'external_ref_ord_delivered' => __('External Order Delivered', 'studiou-wc-ord-print-statuses'),
  15. );
  16. }
  17. /**
  18. * Meta key → translatable label for plain text fields.
  19. */
  20. private static function text_fields() {
  21. return array(
  22. 'external_ref_ord_no' => __('External Order Number', 'studiou-wc-ord-print-statuses'),
  23. );
  24. }
  25. public function __construct() {
  26. add_action('woocommerce_admin_order_data_after_order_details', array($this, 'display_custom_order_fields'));
  27. add_action('woocommerce_process_shop_order_meta', array($this, 'save_custom_order_fields'));
  28. }
  29. public function display_custom_order_fields($order) {
  30. ?>
  31. <div class="order_data_column">
  32. <h4><?php esc_html_e('Print Information', 'studiou-wc-ord-print-statuses'); ?></h4>
  33. <?php
  34. foreach (self::datetime_fields() as $key => $label) {
  35. woocommerce_wp_text_input(array(
  36. 'id' => $key,
  37. 'label' => $label,
  38. 'type' => 'datetime-local',
  39. 'value' => $this->mysql_to_datetime_local($order->get_meta($key)),
  40. 'wrapper_class' => 'form-field-wide',
  41. ));
  42. }
  43. foreach (self::text_fields() as $key => $label) {
  44. woocommerce_wp_text_input(array(
  45. 'id' => $key,
  46. 'label' => $label,
  47. 'value' => $order->get_meta($key),
  48. 'wrapper_class' => 'form-field-wide',
  49. ));
  50. }
  51. ?>
  52. </div>
  53. <?php
  54. }
  55. public function save_custom_order_fields($order_id) {
  56. if (!current_user_can('edit_shop_order', $order_id) && !current_user_can('edit_shop_orders')) {
  57. return;
  58. }
  59. $order = wc_get_order($order_id);
  60. if (!$order) {
  61. return;
  62. }
  63. foreach (array_keys(self::datetime_fields()) as $field) {
  64. if (isset($_POST[$field])) {
  65. $order->update_meta_data($field, $this->datetime_local_to_mysql($_POST[$field]));
  66. }
  67. }
  68. foreach (array_keys(self::text_fields()) as $field) {
  69. if (isset($_POST[$field])) {
  70. $order->update_meta_data($field, sanitize_text_field(wp_unslash($_POST[$field])));
  71. }
  72. }
  73. $order->save();
  74. }
  75. /**
  76. * Convert a MySQL datetime string (as written by current_time('mysql')) to
  77. * the HTML5 `datetime-local` format. Done as pure string reformatting so
  78. * the value's timezone is preserved — current_time('mysql') already uses
  79. * WP local time, and the browser interprets datetime-local as local time.
  80. * Using strtotime()/date() here would re-interpret the value in the server
  81. * timezone and drift on UTC-server / non-UTC-site configurations.
  82. */
  83. private function mysql_to_datetime_local($value) {
  84. $value = trim((string) $value);
  85. if ($value === '') {
  86. return '';
  87. }
  88. // Accepts 'YYYY-MM-DD HH:MM[:SS]' or 'YYYY-MM-DDTHH:MM[:SS]'.
  89. if (preg_match('/^(\d{4}-\d{2}-\d{2})[ T](\d{2}:\d{2})/', $value, $m)) {
  90. return $m[1] . 'T' . $m[2];
  91. }
  92. return '';
  93. }
  94. /**
  95. * Convert an HTML5 datetime-local form value to a MySQL datetime string.
  96. * No timezone conversion — the form value and the stored value are both
  97. * treated as WP local time (matching current_time('mysql')).
  98. */
  99. private function datetime_local_to_mysql($value) {
  100. $value = sanitize_text_field(wp_unslash($value));
  101. if ($value === '') {
  102. return '';
  103. }
  104. if (preg_match('/^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2})(?::(\d{2}))?$/', $value, $m)) {
  105. $seconds = isset($m[3]) ? $m[3] : '00';
  106. return $m[1] . ' ' . $m[2] . ':' . $seconds;
  107. }
  108. return '';
  109. }
  110. }