PluginProbe
PostNL for WooCommerce / 4.4.1
PostNL for WooCommerce v4.4.1
5.9.12 5.9.11 5.9.10 5.9.9 5.9.8 5.9.7 5.9.6 trunk 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 3.1.4 3.1.5 3.1.6 3.1.7 4.0.0 4.0.1 4.0.2 4.3.2 4.3.3 4.4.0 4.4.1 All 72 releases
woo-postnl / includes / class-wcpn-postcode-fields.php

class-wcpn-postcode-fields.php in PostNL for WooCommerce 4.4.1, at includes/class-wcpn-postcode-fields.php

1,149 lines 37.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use WPO\WC\PostNL\Compatibility\Order as WCX_Order;
4 use WPO\WC\PostNL\Compatibility\WC_Core as WCX;
5
6 if (! defined('ABSPATH')) {
7 exit;
8 } // Exit if accessed directly
9
10 if (class_exists('WCPN_NL_Postcode_Fields')) {
11 return new WCPN_NL_Postcode_Fields();
12 }
13
14 class WCPN_NL_Postcode_Fields
15 {
16 /*
17 * Regular expression used to split street name from house number.
18 * This regex goes from right to left
19 * Contains php keys to store the data in an array
20 * Taken from https://github.com/myparcel/sdk
21 */
22 public const SPLIT_STREET_REGEX = '~(?P<street>.*?)\s?(?P<street_suffix>(?P<number>[\d]+)[\s-]{0,2}(?P<extension>[a-zA-Z/\s]{0,5}$|[0-9/]{0,5}$|\s[a-zA-Z]{1}[0-9]{0,3}$|\s[0-9]{2}[a-zA-Z]{0,3}$))$~';
23
24 public const COUNTRIES_WITH_SPLIT_ADDRESS_FIELDS = ['NL', 'BE'];
25
26 public function __construct()
27 {
28 // Load styles
29 add_action('wp_enqueue_scripts', [&$this, 'add_styles_scripts']);
30
31 // Load scripts
32 add_action('admin_enqueue_scripts', [&$this, 'admin_scripts_styles']);
33
34 add_action("wp_loaded", [$this, "initialize"], 9999);
35 }
36
37 public function initialize()
38 {
39 if (WCPOST()->setting_collection->isEnabled('use_split_address_fields')) {
40 // Add street name & house number checkout fields.
41 if (version_compare(WOOCOMMERCE_VERSION, '2.0') >= 0) {
42 // WC 2.0 or newer is used, the filter got a $country parameter, yay!
43 add_filter(
44 'woocommerce_billing_fields',
45 [&$this, 'modifyBillingFields'],
46 apply_filters('wcpn_checkout_fields_priority', 10, 'billing'),
47 2
48 );
49 add_filter(
50 'woocommerce_shipping_fields',
51 [&$this, 'modifyShippingFields'],
52 apply_filters('wcpn_checkout_fields_priority', 10, 'shipping'),
53 2
54 );
55 } else {
56 // Backwards compatibility
57 add_filter('woocommerce_billing_fields', [&$this, 'modifyBillingFields']);
58 add_filter('woocommerce_shipping_fields', [&$this, 'modifyShippingFields']);
59 }
60
61 // Localize checkout fields (limit custom checkout fields to NL and NL)
62 add_filter('woocommerce_country_locale_field_selectors', [&$this, 'country_locale_field_selectors']);
63 add_filter('woocommerce_default_address_fields', [&$this, 'default_address_fields']);
64 add_filter('woocommerce_get_country_locale', [&$this, 'woocommerce_locale_be'], 1, 1); // !
65
66 // Load custom order data.
67 add_filter('woocommerce_load_order_data', [&$this, 'load_order_data']);
68
69 // Custom shop_order details.
70 add_filter('woocommerce_admin_billing_fields', [&$this, 'admin_billing_fields']);
71 add_filter('woocommerce_admin_shipping_fields', [&$this, 'admin_shipping_fields']);
72 add_filter('woocommerce_found_customer_details', [$this, 'customer_details_ajax']);
73 add_action('save_post', [&$this, 'save_custom_fields']);
74
75 // add to user profile page
76 add_filter('woocommerce_customer_meta_fields', [&$this, 'user_profile_fields']);
77
78 add_action(
79 'woocommerce_checkout_update_order_meta',
80 [&$this, 'merge_street_number_suffix'],
81 20,
82 2
83 );
84 add_filter(
85 'woocommerce_process_checkout_field_billing_postcode',
86 [&$this, 'clean_billing_postcode']
87 );
88 add_filter(
89 'woocommerce_process_checkout_field_shipping_postcode',
90 [&$this, 'clean_shipping_postcode']
91 );
92
93 // Save the order data in WooCommerce 2.2 or later.
94 if (version_compare(WOOCOMMERCE_VERSION, '2.2') >= 0) {
95 add_action('woocommerce_checkout_update_order_meta', [&$this, 'save_order_data'], 10, 2);
96 }
97
98 // Remove placeholder values (IE8 & 9)
99 add_action('woocommerce_checkout_update_order_meta', [&$this, 'remove_placeholders'], 10, 2);
100
101 // Fix weird required field translations
102 add_filter(
103 'woocommerce_checkout_required_field_notice',
104 [&$this, 'required_field_notices'],
105 10,
106 2
107 );
108
109 $this->load_woocommerce_filters();
110 } else { // if NOT using old fields
111 add_action('woocommerce_after_checkout_validation', [&$this, 'validate_address_fields'], 10, 2);
112 }
113
114 // Processing checkout
115 add_filter('woocommerce_validate_postcode', [&$this, 'validate_postcode'], 10, 3);
116
117 // set later priority for woocommerce_billing_fields / woocommerce_shipping_fields
118 // when Checkout Field Editor is active
119 if (function_exists('thwcfd_is_locale_field')
120 || function_exists(
121 'wc_checkout_fields_modify_billing_fields'
122 )) {
123 add_filter('be_checkout_fields_priority', 1001);
124 }
125
126 // Hide state field for countries without states (backwards compatible fix for bug #4223)
127 if (version_compare(WOOCOMMERCE_VERSION, '2.1', '<')) {
128 add_filter('woocommerce_countries_allowed_country_states', [&$this, 'hide_states']);
129 }
130 }
131
132 public function load_woocommerce_filters()
133 {
134 // Custom address format.
135 if (version_compare(WOOCOMMERCE_VERSION, '2.0.6', '>=')) {
136 add_filter('woocommerce_localisation_address_formats', [$this, 'localisation_address_formats']);
137 add_filter(
138 'woocommerce_formatted_address_replacements',
139 [$this, 'formatted_address_replacements'],
140 1,
141 2
142 );
143 add_filter(
144 'woocommerce_order_formatted_billing_address',
145 [$this, 'order_formatted_billing_address'],
146 1,
147 2
148 );
149 add_filter(
150 'woocommerce_order_formatted_shipping_address',
151 [$this, 'order_formatted_shipping_address'],
152 1,
153 2
154 );
155 add_filter(
156 'woocommerce_user_column_billing_address',
157 [$this, 'user_column_billing_address'],
158 1,
159 2
160 );
161 add_filter(
162 'woocommerce_user_column_shipping_address',
163 [$this, 'user_column_shipping_address'],
164 1,
165 2
166 );
167 add_filter(
168 'woocommerce_my_account_my_address_formatted_address',
169 [$this, 'my_account_my_address_formatted_address'],
170 1,
171 3
172 );
173 }
174 }
175
176 /**
177 * Load styles & scripts.
178 */
179 public function add_styles_scripts()
180 {
181 if (! is_checkout() && ! is_account_page()) {
182 return;
183 }
184
185 // Enqueue styles for delivery options
186 wp_enqueue_style(
187 'checkout',
188 WCPOST()->plugin_url() . '/assets/css/checkout.css',
189 false,
190 WC_POSTNL_VERSION
191 );
192
193 if (! WCPOST()->setting_collection->isEnabled('use_split_address_fields')) {
194 return;
195 }
196
197 if (version_compare(WOOCOMMERCE_VERSION, '2.1', '<=')) {
198 // Backwards compatibility for https://github.com/woothemes/woocommerce/issues/4239
199 wp_register_script(
200 'checkout',
201 WCPOST()->plugin_url() . '/assets/js/checkout.js',
202 ['jquery', 'wc-checkout'],
203 WC_POSTNL_VERSION
204 );
205 wp_enqueue_script('checkout');
206 }
207
208 if (is_account_page()) {
209 // Disable regular address fields for NL on account page - Fixed in WC 2.1 but not on init...
210 wp_register_script(
211 'account-page',
212 WCPOST()->plugin_url() . '/assets/js/account-page.js',
213 ['jquery'],
214 WC_POSTNL_VERSION
215 );
216 wp_enqueue_script('account-page');
217 }
218 }
219
220 /**
221 * Load admin styles & scripts.
222 */
223 public function admin_scripts_styles($hook)
224 {
225 global $post_type;
226 if ($post_type == 'shop_order') {
227 wp_enqueue_style(
228 'checkout-admin',
229 WCPOST()->plugin_url() . '/assets/css/checkout-admin.css',
230 [], // deps
231 WC_POSTNL_VERSION
232 );
233 }
234 }
235
236 /**
237 * Hide default Dutch address fields
238 *
239 * @param array $locale woocommerce country locale field settings
240 *
241 * @return array $locale
242 */
243 public function woocommerce_locale_be(array $locale): array
244 {
245 foreach (self::COUNTRIES_WITH_SPLIT_ADDRESS_FIELDS as $cc) {
246 $locale[$cc]['address_1'] = [
247 'required' => false,
248 'hidden' => true,
249 ];
250
251 $locale[$cc]['address_2'] = [
252 'hidden' => true,
253 ];
254
255 $locale[$cc]['state'] = [
256 'hidden' => true,
257 'required' => false,
258 ];
259
260 $locale[$cc]['street_name'] = [
261 'required' => true,
262 'hidden' => false,
263 ];
264
265 $locale[$cc]['house_number'] = [
266 'required' => true,
267 'hidden' => false,
268 ];
269
270 $locale[$cc]['house_number_suffix'] = [
271 'required' => false,
272 'hidden' => false,
273 ];
274 }
275 return $locale;
276 }
277
278 /**
279 * @param array $fields
280 * @param string $country
281 *
282 * @return array
283 */
284 public function modifyBillingFields(array $fields, string $country = ''): array
285 {
286 return $this->modifyCheckoutFields($fields, $country, 'billing');
287 }
288
289 /**
290 * @param array $fields
291 * @param string $country
292 *
293 * @return array
294 */
295 public function modifyShippingFields(array $fields, string $country = ''): array
296 {
297 return $this->modifyCheckoutFields($fields, $country, 'shipping');
298 }
299
300 /**
301 * New checkout billing/shipping fields
302 *
303 * @param array $fields Default fields.
304 *
305 * @param string $country
306 * @param string $form
307 *
308 * @return array $fields New fields.
309 */
310 public function modifyCheckoutFields(array $fields, string $country, string $form): array
311 {
312 if (isset($fields['_country'])) {
313 // some weird bug on the my account page
314 $form = '';
315 }
316
317 // Set required to true if country is using custom address fields
318 $required = self::isCountryWithSplitAddressFields($country);
319
320 // Add street name
321 $fields[$form . '_street_name'] = [
322 'label' => __("Street name", "woocommerce-postnl"),
323 'class' => apply_filters('wcpn_custom_address_field_class', ['form-row-third first']),
324 'required' => $required, // Only required for NL
325 'priority' => 60,
326 ];
327
328 // Add house number
329 $fields[$form . '_house_number'] = [
330 'label' => __("No.", "woocommerce-postnl"),
331 'class' => apply_filters('wcpn_custom_address_field_class', ['form-row-third']),
332 'required' => $required, // Only required for NL
333 'type' => 'number',
334 'priority' => 61,
335 ];
336
337 // Add house number suffix
338 $fields[$form . '_house_number_suffix'] = [
339 'label' => __("Suffix", "woocommerce-postnl"),
340 'class' => apply_filters('wcpn_custom_address_field_class', ['form-row-third last']),
341 'required' => false,
342 'maxlength' => 4,
343 'priority' => 62,
344 ];
345
346 // Create new ordering for checkout fields
347 $order_keys = [
348 $form . '_first_name',
349 $form . '_last_name',
350 $form . '_company',
351 $form . '_country',
352 $form . '_address_1',
353 $form . '_address_2',
354 $form . '_street_name',
355 $form . '_house_number',
356 $form . '_house_number_suffix',
357 $form . '_postcode',
358 $form . '_city',
359 $form . '_state',
360 ];
361
362 if ($form === 'billing') {
363 array_push(
364 $order_keys,
365 $form . '_email',
366 $form . '_phone'
367 );
368 }
369
370 $new_order = [];
371
372 // Create reordered array and fill with old array values
373 foreach ($order_keys as $key) {
374 $new_order[$key] = $fields[$key];
375 }
376
377 // Merge (&overwrite) field array
378 $fields = array_merge($new_order, $fields);
379
380 return $fields;
381 }
382
383 /**
384 * Hide state field for countries without states (backwards compatible fix for WooCommerce bug #4223)
385 *
386 * @param array $allowed_states states per country
387 *
388 * @return array
389 */
390 public function hide_states($allowed_states)
391 {
392 $hidden_states = [
393 'AF' => [],
394 'AT' => [],
395 'BE' => [],
396 'BI' => [],
397 'CZ' => [],
398 'DE' => [],
399 'DK' => [],
400 'FI' => [],
401 'FR' => [],
402 'HU' => [],
403 'IS' => [],
404 'IL' => [],
405 'KR' => [],
406 'NL' => [],
407 'NO' => [],
408 'PL' => [],
409 'PT' => [],
410 'SG' => [],
411 'SK' => [],
412 'SI' => [],
413 'LK' => [],
414 'SE' => [],
415 'VN' => [],
416 ];
417 return $hidden_states + $allowed_states;
418 }
419
420 /**
421 * Localize checkout fields live
422 *
423 * @param array $locale_fields list of fields filtered by locale
424 *
425 * @return array $locale_fields with custom fields added
426 */
427 public function country_locale_field_selectors($locale_fields)
428 {
429 $custom_locale_fields = [
430 'street_name' => '#billing_street_name_field, #shipping_street_name_field',
431 'house_number' => '#billing_house_number_field, #shipping_house_number_field',
432 'house_number_suffix' => '#billing_house_number_suffix_field, #shipping_house_number_suffix_field',
433 ];
434
435 $locale_fields = array_merge($locale_fields, $custom_locale_fields);
436
437 return $locale_fields;
438 }
439
440 /**
441 * Make NL checkout fields hidden by default
442 *
443 * @param array $fields default checkout fields
444 *
445 * @return array $fields default + custom checkout fields
446 */
447 public function default_address_fields($fields)
448 {
449 $custom_fields = [
450 'street_name' => [
451 'hidden' => true,
452 'required' => false,
453 ],
454 'house_number' => [
455 'hidden' => true,
456 'required' => false,
457 ],
458 'house_number_suffix' => [
459 'hidden' => true,
460 'required' => false,
461 ],
462 ];
463
464 $fields = array_merge($fields, $custom_fields);
465
466 return $fields;
467 }
468
469 /**
470 * Load order custom data.
471 *
472 * @param array $data Default WC_Order data.
473 *
474 * @return array Custom WC_Order data.
475 */
476 public function load_order_data($data)
477 {
478 // Billing
479 $data['billing_street_name'] = '';
480 $data['billing_house_number'] = '';
481 $data['billing_house_number_suffix'] = '';
482
483 // Shipping
484 $data['shipping_street_name'] = '';
485 $data['shipping_house_number'] = '';
486 $data['shipping_house_number_suffix'] = '';
487
488 return $data;
489 }
490
491 /**
492 * Custom billing admin edit fields.
493 *
494 * @param array $fields Default WC_Order data.
495 *
496 * @return array Custom WC_Order data.
497 */
498 public function admin_billing_fields($fields)
499 {
500 $fields['street_name'] = [
501 'label' => __("Street name", "woocommerce-postnl"),
502 'show' => true,
503 ];
504
505 $fields['house_number'] = [
506 'label' => __("Number", "woocommerce-postnl"),
507 'show' => true,
508 ];
509
510 $fields['house_number_suffix'] = [
511 'label' => __("Suffix", "woocommerce-postnl"),
512 'show' => true,
513 ];
514
515 return $fields;
516 }
517
518 /**
519 * Custom shipping admin edit fields.
520 *
521 * @param array $fields Default WC_Order data.
522 *
523 * @return array Custom WC_Order data.
524 */
525 public function admin_shipping_fields($fields)
526 {
527 $fields['street_name'] = [
528 'label' => __("Street name", "woocommerce-postnl"),
529 'show' => true,
530 ];
531
532 $fields['house_number'] = [
533 'label' => __("Number", "woocommerce-postnl"),
534 'show' => true,
535 ];
536
537 $fields['house_number_suffix'] = [
538 'label' => __("Suffix", "woocommerce-postnl"),
539 'show' => true,
540 ];
541
542 return $fields;
543 }
544
545 /**
546 * Custom user profile edit fields.
547 */
548 public function user_profile_fields($meta_fields)
549 {
550 $postnl_billing_fields = [
551 'billing_street_name' => [
552 'label' => __("Street", "woocommerce-postnl"),
553 'description' => '',
554 ],
555 'billing_house_number' => [
556 'label' => __("Number", "woocommerce-postnl"),
557 'description' => '',
558 ],
559 'billing_house_number_suffix' => [
560 'label' => __("Suffix", "woocommerce-postnl"),
561 'description' => '',
562 ],
563 ];
564 $postnl_shipping_fields = [
565 'shipping_street_name' => [
566 'label' => __("Street", "woocommerce-postnl"),
567 'description' => '',
568 ],
569 'shipping_house_number' => [
570 'label' => __("Number", "woocommerce-postnl"),
571 'description' => '',
572 ],
573 'shipping_house_number_suffix' => [
574 'label' => __("Suffix", "woocommerce-postnl"),
575 'description' => '',
576 ],
577 ];
578
579 // add postnl fields to billing section
580 $billing_fields = array_merge(
581 $meta_fields['billing']['fields'],
582 $postnl_billing_fields
583 );
584 $billing_fields = $this->array_move_keys(
585 $billing_fields,
586 ['billing_street_name', 'billing_house_number', 'billing_house_number_suffix'],
587 'billing_address_2',
588 'after'
589 );
590 $meta_fields['billing']['fields'] = $billing_fields;
591
592 // add postnl fields to shipping section
593 $shipping_fields = array_merge(
594 $meta_fields['shipping']['fields'],
595 $postnl_shipping_fields
596 );
597 $shipping_fields = $this->array_move_keys(
598 $shipping_fields,
599 ['shipping_street_name', 'shipping_house_number', 'shipping_house_number_suffix'],
600 'shipping_address_2',
601 'after'
602 );
603 $meta_fields['shipping']['fields'] = $shipping_fields;
604
605 return $meta_fields;
606 }
607
608 /**
609 * Add custom fields in customer details ajax.
610 * called when clicking the "Load billing/shipping address" button on Edit Order view
611 *
612 * @return array
613 */
614 public function customer_details_ajax($customer_data)
615 {
616 $user_id = (int) trim(stripslashes($_POST['user_id']));
617 $type_to_load = esc_attr(trim(stripslashes($_POST['type_to_load'])));
618
619 $custom_data = [
620 $type_to_load . '_street_name' => get_user_meta($user_id, $type_to_load . '_street_name', true),
621 $type_to_load . '_house_number' => get_user_meta(
622 $user_id,
623 $type_to_load . '_house_number',
624 true
625 ),
626 $type_to_load . '_house_number_suffix' => get_user_meta(
627 $user_id,
628 $type_to_load . '_house_number_suffix',
629 true
630 ),
631 ];
632
633 return array_merge($customer_data, $custom_data);
634 }
635
636 /**
637 * Save custom fields from admin.
638 */
639 public function save_custom_fields($post_id)
640 {
641 $post_type = get_post_type($post_id);
642 if (($post_type == 'shop_order' || $post_type == 'shop_order_refund') && ! empty($_POST)) {
643 $order = WCX::get_order($post_id);
644 $addresses = ['billing', 'shipping'];
645 $address_fields = ['street_name', 'house_number', 'house_number_suffix'];
646 foreach ($addresses as $address) {
647 foreach ($address_fields as $address_field) {
648 if (isset($_POST["_{$address}_{$address_field}"])) {
649 WCX_Order::update_meta_data(
650 $order,
651 "_{$address}_{$address_field}",
652 stripslashes($_POST["_{$address}_{$address_field}"])
653 );
654 }
655 }
656 }
657 }
658
659 return;
660 }
661
662 /**
663 * Merge street name, street number and street suffix into the default 'address_1' field
664 *
665 * @param mixed $order_id Order ID of checkout order.
666 *
667 * @return void
668 */
669 public function merge_street_number_suffix($order_id): void
670 {
671 $order = WCX::get_order($order_id);
672 $billingHasCustomAddressFields = self::isCountryWithSplitAddressFields($_POST['billing_country']);
673 $shippingHasCustomAddressFields = self::isCountryWithSplitAddressFields($_POST['shipping_country']);
674
675 if (version_compare(WOOCOMMERCE_VERSION, '2.1', '<=')) {
676 // old versions use 'shiptobilling'
677 $shipToDifferentAddress = ! isset($_POST['shiptobilling']);
678 } else {
679 // WC2.1
680 $shipToDifferentAddress = isset($_POST['ship_to_different_address']);
681 }
682
683 if ($billingHasCustomAddressFields) {
684 // concatenate street & house number & copy to 'billing_address_1'
685 $suffix = ! empty($_POST['billing_house_number_suffix'])
686 ? '-' . $_POST['billing_house_number_suffix']
687 : '';
688
689 $billingHouseNumber = $_POST['billing_house_number'] . $suffix;
690 $billingAddress1 = $_POST['billing_street_name'] . ' ' . $billingHouseNumber;
691 WCX_Order::set_address_prop($order, 'address_1', 'billing', $billingAddress1);
692
693 if (! $shipToDifferentAddress && $this->cart_needs_shipping_address()) {
694 // use billing address
695 WCX_Order::set_address_prop($order, 'address_1', 'shipping', $billingAddress1);
696 }
697 }
698
699 if ($shippingHasCustomAddressFields && $shipToDifferentAddress) {
700 // concatenate street & house number & copy to 'shipping_address_1'
701 $suffix = ! empty($_POST['shipping_house_number_suffix'])
702 ? '-' . $_POST['shipping_house_number_suffix']
703 : '';
704
705 $shippingHouseNumber = $_POST['shipping_house_number'] . $suffix;
706 $shippingAddress1 = $_POST['shipping_street_name'] . ' ' . $shippingHouseNumber;
707 WCX_Order::set_address_prop($order, 'address_1', 'shipping', $shippingAddress1);
708 }
709 }
710
711 /**
712 * validate NL postcodes
713 *
714 * @return bool $valid
715 */
716 public function validate_postcode($valid, $postcode, $country)
717 {
718 if ($country == 'NL') {
719 $valid = (bool) preg_match('/^[1-9][0-9]{3}/i', trim($postcode));
720 }
721
722 return $valid;
723 }
724
725 /**
726 * validate address field 1 for shipping and billing
727 */
728 public function validate_address_fields($address, $errors)
729 {
730 if (self::isCountryWithSplitAddressFields($address['billing_country'])
731 && ! (bool) preg_match(
732 self::SPLIT_STREET_REGEX,
733 trim(
734 $address['billing_address_1']
735 )
736 )) {
737 $errors->add('address', __("Please enter a valid billing address.", "woocommerce-postnl"));
738 }
739
740 if (self::isCountryWithSplitAddressFields($address['shipping_country'])
741 && ! empty($address['ship_to_different_address'])
742 && ! (bool) preg_match(
743 self::SPLIT_STREET_REGEX,
744 trim(
745 $address['shipping_address_1']
746 )
747 )) {
748 $errors->add('address', __("Please enter a valid shipping address.", "woocommerce-postnl"));
749 }
750 }
751
752 /**
753 * Clean postcodes : remove space, dashes (& other non alphanumeric characters)
754 *
755 * @return $billing_postcode
756 * @return $shipping_postcode
757 */
758 public function clean_billing_postcode()
759 {
760 if ($_POST['billing_country'] == 'NL') {
761 $billing_postcode = preg_replace('/[^a-zA-Z0-9]/', '', $_POST['billing_postcode']);
762 } else {
763 $billing_postcode = $_POST['billing_postcode'];
764 }
765
766 return $billing_postcode;
767 }
768
769 public function clean_shipping_postcode()
770 {
771 if ($_POST['billing_country'] == 'NL') {
772 $shipping_postcode = preg_replace('/[^a-zA-Z0-9]/', '', $_POST['shipping_postcode']);
773 } else {
774 $shipping_postcode = $_POST['shipping_postcode'];
775 }
776
777 return $shipping_postcode;
778 }
779
780 /**
781 * Remove placeholders from posted checkout data
782 *
783 * @param string $order_id order_id of the new order
784 * @param array $posted Array of posted form data
785 *
786 * @return void
787 */
788 public function remove_placeholders($order_id, $posted)
789 {
790 $order = WCX::get_order($order_id);
791 // get default address fields with their placeholders
792 $countries = new WC_Countries();
793 $fields = $countries->get_default_address_fields();
794
795 // define order_comments placeholder
796 $order_comments_placeholder = _x(
797 'Notes about your order, e.g. special notes for delivery.',
798 'placeholder',
799 'woocommerce'
800 );
801
802 // check if ship to billing is set
803 if (version_compare(WOOCOMMERCE_VERSION, '2.1', '<=')) {
804 // old versions use 'shiptobilling'
805 $ship_to_different_address = isset($_POST['shiptobilling']) ? false : true;
806 } else {
807 // WC2.1
808 $ship_to_different_address = isset($_POST['ship_to_different_address']) ? true : false;
809 }
810
811 // check the billing & shipping fields
812 $field_types = ['billing', 'shipping'];
813 $check_fields = ['address_1', 'address_2', 'city', 'state', 'postcode'];
814 foreach ($field_types as $field_type) {
815 foreach ($check_fields as $check_field) {
816 if (isset($posted[$field_type . '_' . $check_field])
817 && isset($fields[$check_field]['placeholder'])
818 && $posted[$field_type . '_' . $check_field] == $fields[$check_field]['placeholder']) {
819 WCX_Order::set_address_prop($order, $check_field, $field_type, '');
820
821 // also clear shipping field when ship_to_different_address is false
822 if ($ship_to_different_address == false && $field_type == 'billing') {
823 WCX_Order::set_address_prop($order, $check_field, 'shipping', '');
824 }
825 }
826 }
827 }
828
829 // check the order comments field
830 if ($posted['order_comments'] == $order_comments_placeholder) {
831 wp_update_post(
832 [
833 'ID' => $order_id,
834 'post_excerpt' => '',
835 ]
836 );
837 }
838
839 return;
840 }
841
842 /**
843 * WooCommerce concatenates translations for required field notices that result in
844 * confusing messages, so we translate the full notice to prevent this
845 */
846 public function required_field_notices($notice, $field_label)
847 {
848 // concatenate translations
849 $billing_nr = sprintf(__("Billing %s", "woocommerce"), __("No."));
850 $shipping_nr = sprintf(__("Shipping %s", "woocommerce"), __("No."));
851
852 switch ($field_label) {
853 case $billing_nr:
854 $notice = __("<b>Billing No.</b> is a required field", "woocommerce-postnl");
855 break;
856 case $shipping_nr:
857 $notice = __("<b>Shipping No.</b> is a required field", "woocommerce-postnl");
858 break;
859 default:
860 break;
861 }
862
863 return $notice;
864 }
865
866 /**
867 * Custom country address formats.
868 *
869 * @param array $formats Defaul formats.
870 *
871 * @return array New NL format.
872 */
873 public function localisation_address_formats($formats)
874 {
875 $formats['NL'] = "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}";
876
877 return $formats;
878 }
879
880 /**
881 * Custom country address format.
882 *
883 * @param array $replacements Default replacements.
884 * @param array $args Arguments to replace.
885 *
886 * @return array New replacements.
887 */
888 public function formatted_address_replacements(array $replacements, array $args): array
889 {
890 $country = $args['country'] ?? null;
891 $house_number = $args['house_number'] ?? null;
892 $house_number_suffix = $args['house_number_suffix'] ?? null;
893 $street_name = $args['street_name'] ?? null;
894
895 if (! empty($street_name) && self::isCountryWithSplitAddressFields($country)) {
896 $replacements['{address_1}'] = $street_name . ' ' . $house_number . $house_number_suffix;
897 }
898
899 return $replacements;
900 }
901
902 /**
903 * Custom order formatted billing address.
904 *
905 * @param array $address Default address.
906 * @param object $order Order data.
907 *
908 * @return array New address format.
909 */
910 public function order_formatted_billing_address($address, $order)
911 {
912 $address['street_name'] = WCX_Order::get_meta($order, '_billing_street_name', true, 'view');
913 $address['house_number'] = WCX_Order::get_meta($order, '_billing_house_number', true, 'view');
914 $address['house_number_suffix'] = WCX_Order::get_meta($order, '_billing_house_number_suffix', true, 'view');
915 $address['house_number_suffix'] =
916 ! empty($address['house_number_suffix']) ? '-' . $address['house_number_suffix'] : '';
917
918 return $address;
919 }
920
921 /**
922 * Custom order formatted shipping address.
923 *
924 * @param array $address Default address.
925 * @param object $order Order data.
926 *
927 * @return array New address format.
928 */
929 public function order_formatted_shipping_address($address, $order)
930 {
931 $address['street_name'] = WCX_Order::get_meta($order, '_shipping_street_name', true, 'view');
932 $address['house_number'] = WCX_Order::get_meta($order, '_shipping_house_number', true, 'view');
933 $address['house_number_suffix'] = WCX_Order::get_meta(
934 $order,
935 '_shipping_house_number_suffix',
936 true,
937 'view'
938 );
939 $address['house_number_suffix'] =
940 ! empty($address['house_number_suffix']) ? '-' . $address['house_number_suffix'] : '';
941
942 return $address;
943 }
944
945 /**
946 * Custom user column billing address information.
947 *
948 * @param array $address Default address.
949 * @param int $user_id User id.
950 *
951 * @return array New address format.
952 */
953 public function user_column_billing_address($address, $user_id)
954 {
955 $address['street_name'] = get_user_meta($user_id, 'billing_street_name', true);
956 $address['house_number'] = get_user_meta($user_id, 'billing_house_number', true);
957 $address['house_number_suffix'] =
958 (get_user_meta($user_id, 'billing_house_number_suffix', true)) ? '-' . get_user_meta(
959 $user_id,
960 'billing_house_number_suffix',
961 true
962 ) : '';
963
964 return $address;
965 }
966
967 /**
968 * Custom user column shipping address information.
969 *
970 * @param array $address Default address.
971 * @param int $user_id User id.
972 *
973 * @return array New address format.
974 */
975 public function user_column_shipping_address($address, $user_id)
976 {
977 $address['street_name'] = get_user_meta($user_id, 'shipping_street_name', true);
978 $address['house_number'] = get_user_meta($user_id, 'shipping_house_number', true);
979 $address['house_number_suffix'] =
980 (get_user_meta($user_id, 'shipping_house_number_suffix', true)) ? '-' . get_user_meta(
981 $user_id,
982 'shipping_house_number_suffix',
983 true
984 ) : '';
985
986 return $address;
987 }
988
989 /**
990 * Custom my address formatted address.
991 *
992 * @param array $address Default address.
993 * @param int $customer_id Customer ID.
994 * @param string $name Field name (billing or shipping).
995 *
996 * @return array New address format.
997 */
998 public function my_account_my_address_formatted_address($address, $customer_id, $name)
999 {
1000 $address['street_name'] = get_user_meta($customer_id, $name . '_street_name', true);
1001 $address['house_number'] = get_user_meta($customer_id, $name . '_house_number', true);
1002 $address['house_number_suffix'] =
1003 (get_user_meta($customer_id, $name . '_house_number_suffix', true)) ? '-' . get_user_meta(
1004 $customer_id,
1005 $name . '_house_number_suffix',
1006 true
1007 ) : '';
1008
1009 return $address;
1010 }
1011
1012 /**
1013 * Get a posted address field after sanitization and validation.
1014 *
1015 * @param string $key
1016 * @param string $type billing for shipping
1017 *
1018 * @return string
1019 */
1020 public function get_posted_address_data($key, $posted, $type = 'billing')
1021 {
1022 if ('billing' === $type
1023 || (! $posted['ship_to_different_address']
1024 && $this->cart_needs_shipping_address())) {
1025 $return = isset($posted['billing_' . $key]) ? $posted['billing_' . $key] : '';
1026 } elseif ('shipping' === $type && ! $this->cart_needs_shipping_address()) {
1027 $return = '';
1028 } else {
1029 $return = isset($posted['shipping_' . $key]) ? $posted['shipping_' . $key] : '';
1030 }
1031
1032 return $return;
1033 }
1034
1035 public function cart_needs_shipping_address()
1036 {
1037 if (is_object(WC()->cart) && method_exists(WC()->cart, 'needs_shipping_address')
1038 && function_exists('wc_ship_to_billing_address_only')) {
1039 if (WC()->cart->needs_shipping_address() || wc_ship_to_billing_address_only()) {
1040 return true;
1041 } else {
1042 return false;
1043 }
1044 } else {
1045 return true;
1046 }
1047 }
1048
1049 /**
1050 * Save order data.
1051 *
1052 * @param int $order_id
1053 * @param array $posted
1054 *
1055 * @return void
1056 */
1057 public function save_order_data($order_id, $posted)
1058 {
1059 $order = WCX::get_order($order_id);
1060 // Billing.
1061 WCX_Order::update_meta_data(
1062 $order,
1063 '_billing_street_name',
1064 $this->get_posted_address_data('street_name', $posted)
1065 );
1066 WCX_Order::update_meta_data(
1067 $order,
1068 '_billing_house_number',
1069 $this->get_posted_address_data('house_number', $posted)
1070 );
1071 WCX_Order::update_meta_data(
1072 $order,
1073 '_billing_house_number_suffix',
1074 $this->get_posted_address_data('house_number_suffix', $posted)
1075 );
1076
1077 // Shipping.
1078 WCX_Order::update_meta_data(
1079 $order,
1080 '_shipping_street_name',
1081 $this->get_posted_address_data('street_name', $posted, 'shipping')
1082 );
1083 WCX_Order::update_meta_data(
1084 $order,
1085 '_shipping_house_number',
1086 $this->get_posted_address_data('house_number', $posted, 'shipping')
1087 );
1088 WCX_Order::update_meta_data(
1089 $order,
1090 '_shipping_house_number_suffix',
1091 $this->get_posted_address_data('house_number_suffix', $posted, 'shipping')
1092 );
1093 }
1094
1095 /**
1096 * Helper function to move array elements (one or more) to a position before a specific key
1097 *
1098 * @param array $array Main array to modify
1099 * @param mixed $keys Single key or array of keys of element(s) to move
1100 * @param string $reference_key key to put elements before or after
1101 * @param string $position before or after
1102 *
1103 * @return array reordered array
1104 */
1105 public function array_move_keys($array, $keys, $reference_key, $position = 'before')
1106 {
1107 // cast $key as array
1108 $keys = (array) $keys;
1109
1110 if (! isset($array[$reference_key])) {
1111 return $array;
1112 }
1113
1114 $move = [];
1115 foreach ($keys as $key) {
1116 if (! isset($array[$key])) {
1117 continue;
1118 }
1119 $move[$key] = $array[$key];
1120 unset ($array[$key]);
1121 }
1122
1123 if ($position == 'before') {
1124 $move_to_pos = array_search($reference_key, array_keys($array));
1125 } else { // after
1126 $move_to_pos = array_search($reference_key, array_keys($array)) + 1;
1127 }
1128
1129 return array_slice($array, 0, $move_to_pos, true) + $move + array_slice(
1130 $array,
1131 $move_to_pos,
1132 null,
1133 true
1134 );
1135 }
1136
1137 /**
1138 * @param string|null $country
1139 *
1140 * @return bool
1141 */
1142 private static function isCountryWithSplitAddressFields(?string $country): bool
1143 {
1144 return in_array($country, self::COUNTRIES_WITH_SPLIT_ADDRESS_FIELDS);
1145 }
1146 }
1147
1148 return new WCPN_NL_Postcode_Fields();
1149