PluginProbe
PostNL for WooCommerce / trunk
PostNL for WooCommerce vtrunk
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 4.4.2 All 71 releases
woo-postnl / src / Frontend / Base.php

Base.php in PostNL for WooCommerce trunk, at src/Frontend/Base.php

572 lines 14.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Frontend/Base file.
4 *
5 * @package PostNLWooCommerce\Frontend
6 */
7
8 namespace PostNLWooCommerce\Frontend;
9
10 use PostNLWooCommerce\Shipping_Method\Settings;
11 use PostNLWooCommerce\Utils;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 /**
18 * Class Base
19 *
20 * @package PostNLWooCommerce\Frontend
21 */
22 abstract class Base {
23 /**
24 * Settings class instance.
25 *
26 * @var PostNLWooCommerce\Shipping_Method\Settings
27 */
28 protected $settings;
29
30 /**
31 * Template file name.
32 *
33 * @var string
34 */
35 public $template_file;
36
37 /**
38 * Prefix for meta box fields.
39 *
40 * @var prefix
41 */
42 protected $prefix = POSTNL_SETTINGS_ID . '_';
43
44 /**
45 * Primary field name.
46 *
47 * @var primary_field
48 */
49 protected $primary_field;
50
51 /**
52 * Prefix for meta box fields.
53 *
54 * @var meta_name
55 */
56 protected $meta_name;
57
58 /**
59 * Prefix for meta box fields.
60 *
61 * @var meta_name
62 */
63 protected $letterbox_meta_name;
64
65 /**
66 * Init and hook in the integration.
67 */
68 public function __construct() {
69 $this->settings = Settings::get_instance();
70 $this->meta_name = '_' . $this->prefix . 'order_metadata';
71 $this->letterbox_meta_name = '_' . $this->prefix . 'letterbox';
72 $this->set_template_file();
73 $this->set_primary_field_name();
74 $this->init_hooks();
75 }
76
77 /**
78 * Need to set the primary field name;
79 */
80 abstract public function set_primary_field_name();
81
82 /**
83 * Need to set the template file name;
84 */
85 abstract public function set_template_file();
86
87 /**
88 * List of frontend fields.
89 */
90 abstract public function get_fields();
91
92 /**
93 * Check if this feature is enabled from the settings.
94 *
95 * @return bool
96 */
97 abstract public function is_enabled();
98
99 /**
100 * Collection of hooks when initiation.
101 */
102 public function init_hooks() {
103
104 add_filter( 'woocommerce_checkout_posted_data', array( $this, 'validate_posted_data' ) );
105 add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'save_data' ), 10, 2 );
106 add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'save_letterbox_data' ), 13, 2 );
107 add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'save_letterbox_type' ), 14, 2 );
108 add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'save_default_data' ), 15, 2 );
109 add_filter( 'postnl_frontend_checkout_tab', array( $this, 'add_checkout_tab' ), 10, 2 );
110 add_action( 'postnl_checkout_content', array( $this, 'display_content' ), 10, 2 );
111 add_filter( 'woocommerce_email_order_meta_fields', array( $this, 'add_pickup_points_fields_to_email' ), 10, 3 );
112 }
113
114 /**
115 * Adding a tab in the frontend checkout.
116 *
117 * @param array $tabs List of displayed tabs.
118 * @param array $response Response from PostNL Checkout Rest API.
119 *
120 * @return array
121 */
122 abstract public function add_checkout_tab( $tabs, $response );
123
124 /**
125 * Adding a content in the frontend checkout.
126 *
127 * @param array $response Response from PostNL Checkout Rest API.
128 * @param array $post_data Post data on checkout page.
129 */
130 abstract public function get_content_data( $response, $post_data );
131
132 /**
133 * Add pickup points info to email templates.
134 *
135 * @param array $fields Current fields.
136 * @param bool $sent_to_admin If should sent to admin.
137 * @param \WC_Order $order Order instance.
138 *
139 * @return array
140 */
141 public function add_pickup_points_fields_to_email( $fields, $sent_to_admin, $order ) {
142 $data = $this->get_data( $order->get_id() );
143 if ( ! empty( $data['frontend'] ) ) {
144 $value = $this->generate_pickup_points_email_html( $data['frontend'] );
145 $fields['Pickup address'] = array(
146 'label' => __( 'Pick up at PostNL-point', 'postnl-for-woocommerce' ),
147 'value' => $value,
148 );
149 }
150
151 return $fields;
152 }
153
154 /**
155 * Generate the dropoff points html information.
156 *
157 * @param Array $infos Dropoff points informations.
158 */
159 public function generate_pickup_points_email_html( $infos ) {
160 $filtered_infos = Utils::get_filtered_pickup_points_infos( $infos );
161
162 if ( empty( $filtered_infos ) ) {
163 return '';
164 }
165
166 $value = "<div class='postnl-info-container pickup-points-info'>";
167
168 foreach ( $filtered_infos as $info_idx => $info_val ) {
169 switch ( $info_idx ) {
170 case 'dropoff_points_date':
171 $additional_text = esc_html__( 'Date:', 'postnl-for-woocommerce' );
172 break;
173
174 case 'dropoff_points_time':
175 $additional_text = esc_html__( 'Time:', 'postnl-for-woocommerce' );
176 break;
177
178 default:
179 $additional_text = '';
180 break;
181 }
182
183 $value .= '
184 <div>
185 ' . esc_html( $additional_text . ' ' . $info_val ) . '
186 </div>
187 ';
188 }
189
190 $value .= ' </div> <br>';
191
192 return $value;
193 }
194 /**
195 * Adding a content in the frontend checkout.
196 *
197 * @param array $response Response from PostNL Checkout Rest API.
198 * @param array $post_data Post data on checkout page.
199 */
200 public function display_content( $response, $post_data ) {
201 $template_args = array(
202 'data' => $this->get_content_data( $response, $post_data ),
203 );
204
205 wc_get_template( $this->template_file, $template_args, '', POSTNL_WC_PLUGIN_DIR_PATH . '/templates/' );
206 }
207
208 /**
209 * Add value to the fields.
210 *
211 * @return array
212 */
213 public function get_fields_with_value() {
214 $post_data = array();
215
216 if ( isset( $_REQUEST['post_data'] ) ) {
217 parse_str( sanitize_text_field( wp_unslash( $_REQUEST['post_data'] ) ), $post_data );
218 }
219
220 $field_w_val = array_map(
221 function ( $field ) use ( $post_data ) {
222 $field['value'] = array_key_exists( $field['id'], $post_data ) ? $post_data[ $field['id'] ] : '';
223 return $field;
224 },
225 $this->get_fields()
226 );
227
228 return $field_w_val;
229 }
230
231 /**
232 * Validate posted data.
233 *
234 * @param array $data Array of posted data.
235 */
236 public function validate_posted_data( $data ) {
237 $nonce_value = wc_get_var( $_REQUEST['woocommerce-process-checkout-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // phpcs:ignore
238 $expiry_message = sprintf(
239 /* translators: %s: shop cart url */
240 __( 'Sorry, your session has expired. <a href="%s" class="wc-backward">Return to shop</a>', 'postnl-for-woocommerce' ),
241 esc_url( wc_get_page_permalink( 'shop' ) )
242 );
243
244 if ( empty( $nonce_value ) || ! wp_verify_nonce( $nonce_value, 'woocommerce-process_checkout' ) ) {
245 return $data;
246 }
247
248 if ( ! $this->check_selected_option( $_POST ) ) {
249 return $data;
250 }
251
252 $data = $this->validate_fields( $data, $_POST );
253 $data = $this->add_default_value_to_data( $data, $_POST );
254
255 return $data;
256 }
257
258 /**
259 * Add default value to data.
260 *
261 * @param array $data Array of posted data.
262 * @param array $posted_data Array of global _POST data.
263 *
264 * @return array.
265 */
266 public function add_default_value_to_data( $data, $posted_data ) {
267 foreach ( $posted_data as $input_id => $input_val ) {
268 if ( false === strpos( $input_id, 'postnl_default' ) ) {
269 continue;
270 }
271
272 $data[ $input_id ] = $input_val;
273 }
274 return $data;
275 }
276
277 /**
278 * Validate delivery type fields.
279 *
280 * @param array $data Array of posted data.
281 * @param array $posted_data Array of global _POST data.
282 *
283 * @return array
284 */
285 abstract public function validate_fields( $data, $posted_data );
286
287 /**
288 * Check the selected options.
289 *
290 * @param array $posted_data Array of global _POST data.
291 *
292 * @return boolean
293 */
294 public function check_selected_option( $posted_data ) {
295 if ( empty( $posted_data['postnl_option'] ) ) {
296 return false;
297 }
298
299 return ( $posted_data['postnl_option'] === $this->primary_field );
300 }
301
302 /**
303 * Get primary field value.
304 *
305 * @param array $post_data Array of global _POST data.
306 *
307 * @return mixed
308 */
309 public function get_primary_field_value( $post_data ) {
310 $fields = $this->get_fields();
311
312 if ( empty( $post_data['postnl_option'] ) ) {
313 return '';
314 }
315
316 if ( $this->primary_field !== $post_data['postnl_option'] ) {
317 return '';
318 }
319
320 return ( ! empty( $post_data[ $fields['0']['id'] ] ) ) ? $post_data[ $fields['0']['id'] ] : '';
321 }
322
323 /**
324 * Get content data initiation.
325 *
326 * @param array $post_data Array of global _POST data.
327 *
328 * @return array
329 */
330 public function get_init_content_data( $post_data ) {
331 $fields = $this->get_fields();
332 $value = $this->get_primary_field_value( $post_data );
333
334 return array(
335 'field_name' => $fields['0']['id'],
336 'value' => $value,
337 );
338 }
339
340 /**
341 * Get frontend data from Order object.
342 *
343 * @param int $order_id ID of the order.
344 *
345 * @return array.
346 */
347 public function get_data( $order_id ) {
348 $order = wc_get_order( $order_id );
349
350 if ( ! is_a( $order, 'WC_Order' ) ) {
351 return array();
352 }
353
354 $data = $order->get_meta( $this->meta_name );
355 return ! empty( $data ) && is_array( $data ) ? $data : array();
356 }
357
358 /**
359 * Save default value to data if dropoff points is not picked.
360 *
361 * @param array $order_id ID of order post.
362 * @param array $posted_data Array of global _POST data.
363 *
364 * @return array.
365 */
366 public function save_default_data( $order_id, $posted_data ) {
367 $order = wc_get_order( $order_id );
368
369 if ( ! is_a( $order, 'WC_Order' ) ) {
370 return;
371 }
372
373 $data = $this->get_data( $order->get_id() );
374
375 if ( ! empty( $data['frontend'] ) ) {
376 return;
377 }
378
379 foreach ( $posted_data as $input_id => $input_val ) {
380 if ( false === strpos( $input_id, 'postnl_default' ) || empty( $input_val ) ) {
381 continue;
382 }
383
384 $replaced_id = str_replace( 'postnl_default', 'postnl_delivery_day', $input_id );
385 $field_name = Utils::remove_prefix_field( $this->prefix, $replaced_id );
386
387 $data['frontend'][ $field_name ] = $input_val;
388 }
389
390 $order->update_meta_data( $this->meta_name, $data );
391 $order->save();
392 }
393
394 /**
395 * Save frontend field value to meta.
396 *
397 * @param int $order_id ID of the order.
398 * @param array $posted_data Posted values.
399 */
400 public function save_data( $order_id, $posted_data ) {
401 $order = wc_get_order( $order_id );
402
403 if ( ! is_a( $order, 'WC_Order' ) ) {
404 return;
405 }
406
407 $data = $this->get_data( $order->get_id() );
408
409 foreach ( $this->get_fields() as $field ) {
410 if ( array_key_exists( $field['id'], $posted_data ) && ! empty( $posted_data[ $field['id'] ] ) ) {
411 $field_name = Utils::remove_prefix_field( $this->prefix, $field['id'] );
412 $data['frontend'][ $field_name ] = sanitize_text_field( wp_unslash( $posted_data[ $field['id'] ] ) );
413 }
414 }
415
416 $order->update_meta_data( $this->meta_name, $data );
417 $order->save();
418 }
419
420 /**
421 * Check if order is eligible for the letterbox and save it as order meta.
422 *
423 * @param int $order_id ID of the order.
424 * @param array $posted_data Posted values.
425 */
426 public function save_letterbox_data( $order_id, $posted_data ) {
427 $order = wc_get_order( $order_id );
428
429 if ( ! is_a( $order, 'WC_Order' ) ) {
430 return;
431 }
432
433 $eligible_for_letterbox = Utils::is_order_eligible_auto_letterbox( $order );
434
435 if ( $eligible_for_letterbox ) {
436 $order->update_meta_data( $this->letterbox_meta_name, 1 );
437 $order->save();
438 }
439 }
440
441 /**
442 * Save the letterbox type from the selected shipping method.
443 *
444 * @since 5.9.6
445 *
446 * @param int $order_id ID of the order.
447 * @param array $posted_data Posted values.
448 */
449 public function save_letterbox_type( $order_id, $posted_data ) {
450 $order = wc_get_order( $order_id );
451
452 if ( ! is_a( $order, 'WC_Order' ) ) {
453 return;
454 }
455
456 // Get the shipping methods from the order.
457 $shipping_methods = $order->get_shipping_methods();
458
459 if ( empty( $shipping_methods ) ) {
460 return;
461 }
462
463 foreach ( $shipping_methods as $shipping_item ) {
464 // Check if this is a PostNL shipping method.
465 if ( ! in_array( $shipping_item->get_method_id(), $this->settings->get_supported_shipping_methods(), true ) ) {
466 continue;
467 }
468
469 // Check if the rate has letterbox type in meta data.
470 $meta_data = $shipping_item->get_meta_data();
471 foreach ( $meta_data as $meta ) {
472 if ( 'letterbox_type' === $meta->key && in_array( $meta->value, array( 'letterbox', 'letterbox_48' ), true ) ) {
473 $order->update_meta_data( '_postnl_letterbox_type', $meta->value );
474 // Persist the meta explicitly. Core saves the order BEFORE this
475 // hook fires and passes only the order ID, so the in-memory change
476 // above would otherwise be discarded. save_meta_data() writes just
477 // the meta row and does not fire the full woocommerce_update_order
478 // save cycle.
479 $order->save_meta_data();
480 return;
481 }
482 }
483
484 }
485 }
486
487 /**
488 * Get evening fee data.
489 *
490 * @return array
491 */
492 public static function evening_fee_data() {
493 $settings = Settings::get_instance();
494 $evening_fee = $settings->get_evening_delivery_fee();
495
496 return array(
497 'fee_name' => esc_html__( 'PostNL Evening Fee', 'postnl-for-woocommerce' ),
498 'fee_price' => floatval( $evening_fee ),
499 'condition' => array(
500 'key' => 'delivery_day_type',
501 'value' => 'Evening',
502 ),
503 );
504 }
505
506 /**
507 * Get morning fee data.
508 *
509 * @return array
510 */
511 public static function morning_fee_data() {
512 $settings = Settings::get_instance();
513 $morning_fee = $settings->get_morning_delivery_fee();
514
515 return array(
516 'fee_name' => esc_html__( 'PostNL Morning Fee', 'postnl-for-woocommerce' ),
517 'fee_price' => floatval( $morning_fee ),
518 'condition' => array(
519 'key' => 'delivery_day_type',
520 'value' => '08:00-12:00',
521 ),
522 );
523 }
524
525 /**
526 * Get available nonstandard delivery time fees data.
527 *
528 * @return array
529 */
530 public static function non_standard_fees_data() {
531 return array(
532 '08:00-12:00' => self::morning_fee_data(),
533 'Evening' => self::evening_fee_data(),
534 );
535 }
536
537 /**
538 * Check whether the chosen PostNL shipping rate has cost = 0.
539 *
540 * Returns true when the currently selected shipping method is a supported
541 * PostNL method and its rate cost is zero — meaning the store's
542 * minimum_for_free_shipping threshold has been reached.
543 *
544 * @return bool
545 */
546 protected function is_postnl_rate_free(): bool {
547 if ( ! WC()->session ) {
548 return false;
549 }
550
551 $chosen = WC()->session->get( 'chosen_shipping_methods', array() );
552 $packages = WC()->shipping()->get_packages();
553 $supported = $this->settings->get_supported_shipping_methods();
554
555 foreach ( $packages as $i => $package ) {
556 $method_key = $chosen[ $i ] ?? '';
557
558 if ( ! isset( $package['rates'][ $method_key ] ) ) {
559 continue;
560 }
561
562 $rate = $package['rates'][ $method_key ];
563
564 if ( in_array( $rate->get_method_id(), $supported, true ) && 0.0 === (float) $rate->cost ) {
565 return true;
566 }
567 }
568
569 return false;
570 }
571 }
572