PluginProbe
MakeCommerce for WooCommerce / 3.2.0
MakeCommerce for WooCommerce v3.2.0
4.1.0 4.0.8 trunk 1.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 2.0.0 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.2.0 2.2.1 2.2.2 All 96 releases
makecommerce / shipping / method / method.php

method.php in MakeCommerce for WooCommerce 3.2.0, at shipping/method/method.php

669 lines 23.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace MakeCommerce\Shipping;
4
5 /**
6 * Creates minimal functionality needed for WooCommerce shipping method
7 *
8 * @since 3.0.0
9 */
10
11 abstract class Method extends \WC_Shipping_Method {
12
13 use \MakeCommerce\Shipping\Method\ShippingClasses;
14
15 public $id;
16 public $instance_id;
17
18 public $supports = array(
19 'shipping-zones',
20 'instance-settings',
21 'instance-settings-modal',
22 'settings'
23 );
24
25 /**
26 * Construct WooCommerce shipping method
27 *
28 * @since 3.0.0
29 */
30 public function __construct( $instance_id = 0 ) {
31
32 //set required properties
33 $this->instance_id = absint( $instance_id );
34 $this->id = $this->identifier . '_' . $this->carrier_id;
35 $this->ext = $this->carrier_id;
36 $this->title = $this->return_method_title();
37 $this->method_title = $this->title;
38 $this->name_ext = $this->title;
39
40 //get settings from WC_Shipping_Method
41 $this->init_settings();
42
43 //set default settings used by all shipping methods
44 $this->set_default_settings();
45
46 //initialize form fields for admin
47 $this->initialize_form_fields();
48
49 //set hooks
50 $this->set_hooks();
51
52 //initilize method type
53 $this->initialize();
54
55 $this->fields_validation( $this->settings );
56 }
57
58 /**
59 * Set hooks used by all WooCommerce shipping methods
60 *
61 * @since 3.0.0
62 */
63 final public function set_hooks() {
64
65 //update options
66 add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
67 add_filter( 'woocommerce_settings_api_sanitized_fields_' . $this->id, array( $this, 'fields_validation' ) );
68
69 //Woocommerce Multilingual overrides (change some titles)
70 if ( \MakeCommerce\i18n::using_language_plugins() ) {
71 add_filter( 'woocommerce_package_rates', array( $this, 'override_label_translation' ), 50 );
72 add_filter( 'woocommerce_order_get_items', array( $this, 'override_shipping_title_translation' ), 50, 2 );
73 }
74
75 //check if a parcelmachine has been chosen in checkout
76 add_action( 'woocommerce_checkout_process', array( $this, 'check_checkout_fields' ) );
77 add_action( 'woocommerce_after_checkout_validation', array( $this, 'check_checkout_fields' ) );
78 }
79
80 /**
81 * Add check for shipping settings
82 * Checks if certain fields are valid
83 *
84 * @since 3.2.0
85 */
86 function fields_validation( $fields ) {
87 // Phone not set, can not validate
88 if ( !isset( $fields['shop_phone'] ) ) {
89 return $fields;
90 }
91 $fields['shop_phone'] = $this->sanitize_phone_number( $fields['shop_phone'] );
92 if ( $fields['shop_phone'] != '' && !$this->valid_phone_number($fields['shop_phone']) ) {
93 add_action( 'admin_notices', array( $this, 'invalid_phone_number_notice' ) );
94 }
95 return $fields;
96 }
97
98 /**
99 * Add notice for invalid phone number
100 * Adds shipping method specific notice to wp admin
101 *
102 * @since 3.2.0
103 */
104 function invalid_phone_number_notice() {
105
106 $user_id = get_current_user_id();
107
108 $unique_id = 'mc_phone_number_notice_dismissed_' . $this->id;
109 $dismiss_href = '?' . $unique_id;
110
111 if ( isset( $_GET ) && !empty( $_GET ) ) {
112 $dismiss_href = '&' . $unique_id;
113 }
114 if ( isset( $_GET[$unique_id] ) ) {
115 add_user_meta( $user_id, $unique_id, 'true', true );
116 }
117
118 if ( get_user_meta( $user_id, $unique_id ) ) {
119 return;
120 }
121
122 echo '
123 <div class="notice notice-error">
124 <p>
125 ' . $this->phone_number_validation_error() . '
126 <a href="admin.php?page=wc-settings&tab=shipping&section='. $this->id . '">' . __('Update here', 'wc_makecommerce_domain' ) . '</a>
127 <a style="float: right; " href="' . $dismiss_href . '">' . __( 'Dismiss', 'wc_makecommerce_domain' ) . '</a>
128 </p>
129 </div>';
130 }
131
132 /**
133 * Add check for parcelmachine selectbox
134 * Checks if something is chosen
135 *
136 * @since 3.0.4
137 */
138 public function check_checkout_fields() {
139
140 //those checks are needed, otherwise the notification will be added more than once
141 static $added1 = false;
142 static $added2 = false;
143
144 $shipping_method = !empty( $_POST['shipping_method'] ) ? $_POST['shipping_method'] : false;
145
146 if ( !empty( $shipping_method[0] ) ) {
147 $shipping_method_ext = explode( ':', $shipping_method[0] );
148 }
149
150 if ( !$added1 && $shipping_method_ext[0] === $this->id && empty( $_POST[$shipping_method_ext[0]] ) && $this->type == "apt" ) {
151 wc_add_notice( __( '<strong>Parcel machine</strong> is a required field.', 'wc_makecommerce_domain' ), 'error' );
152 $added1 = true;
153 }
154
155 $shipping_phone_number = null;
156 if ( isset( $_POST['ship_to_different_address'] ) && isset( $_POST['shipping_phone'] ) ) {
157 $_POST['shipping_phone'] = $this->sanitize_phone_number( $_POST['shipping_phone'] );
158 $shipping_phone_number = $_POST['shipping_phone'];
159 } else {
160 $_POST['billing_phone'] = $this->sanitize_phone_number( $_POST['billing_phone'] );
161 $shipping_phone_number = $_POST['billing_phone'];
162 }
163
164 if ( $shipping_phone_number !== null ) {
165 if ( !$added2 && $shipping_method_ext[0] === $this->id && !$this->valid_phone_number( $shipping_phone_number ) ) {
166 wc_add_notice( $this->phone_number_validation_error(), 'error' );
167 $added2 = true;
168 }
169 }
170 }
171
172 /**
173 * Sanitize phone number
174 *
175 * @since 3.0.4
176 */
177 public function sanitize_phone_number( $phone_number ) {
178
179 $phone_number = filter_var ( $phone_number, FILTER_SANITIZE_NUMBER_INT );
180 $phone_number = trim( $phone_number, '-' );
181 //convert first 2 zeros to + if needed
182 if ( substr( $phone_number, 0, 2 ) == "00" ) {
183 $phone_number = '+' . substr( $phone_number, 2 );
184 }
185
186 return $phone_number;
187 }
188
189 /**
190 * Check if provided phonenumber is valid
191 *
192 * @since 3.0.4
193 */
194 public function valid_phone_number( $phone_number ) {
195
196 //could be that all that we need to check is international format
197 if ( $this->international_number_format ) {
198 return $this->check_international_number( $phone_number );
199 }
200
201 //allow all numbers if validation is not defined
202 if ( !isset( $this->valid_phonenumber_country_codes ) ) {
203 return true;
204 }
205
206 //remove + signs
207 $phone_number = trim( $phone_number, '+' );
208
209 //check if it contains country code
210 foreach ( $this->valid_phonenumber_country_codes as $country_code=>$valid_numbers ) {
211
212 //country code matched
213 if ( substr( $phone_number, 0, strlen( $country_code ) ) == $country_code ) {
214 return $this->check_number( substr( $phone_number, strlen( $country_code ) ), $valid_numbers );
215 }
216 }
217
218 //country code didn't match, there is nothing we can do but check if its valid in any of the provided countries
219 foreach ( $this->valid_phonenumber_country_codes as $country_code=>$valid_numbers ) {
220 if ( $this->check_number( $phone_number, $valid_numbers ) === true ) {
221 return true;
222 }
223 }
224
225 return false;
226 }
227
228 /**
229 * Check if phone number is international format
230 *
231 * This function could be improved. Either by using https://github.com/google/libphonenumber or creating our own library
232 *
233 * @since 3.0.4
234 */
235 public function check_international_number( $number ) {
236
237 //https://en.wikipedia.org/wiki/E.164 (Solomon islands has 8)
238 $min = 8;
239 $max = 15;
240
241 //without going into any more detail we are just going to check if the number starts with a + and has a minimum amount of numbers and a maximum
242 if ( substr( $number, 0, strlen( '+' ) ) && strlen( $number ) >= ( $min + 1 ) && strlen( $number ) <= ( $max + 1 ) ) {
243 return true;
244 }
245
246 return false;
247 }
248
249 /**
250 * Check if number is valid according to an array
251 *
252 * @since 3.0.4
253 */
254 public function check_number( $number, $valid_numbers ) {
255
256 foreach ( $valid_numbers as $start_digits=>$lenghts ) {
257 foreach ( $lenghts as $lenght ) {
258 // If the starting digit(s) matches OR it is '*' for any starting number
259 // AND the length matches
260 if ( ( substr( $number, 0, strlen( $start_digits ) ) == $start_digits || $start_digits === '*' ) && strlen( $number ) == $lenght ) {
261 return true;
262 }
263 }
264 }
265
266 return false;
267 }
268
269 /**
270 * Label translation override for WPML (woocommerce_package_rates)
271 *
272 * @since 3.0.0
273 */
274 public function override_label_translation( $rates ) {
275
276 $id_instance = $this->id . ':' . $this->instance_id;
277
278 if ( array_key_exists( $id_instance, $rates ) ) {
279 $rates[$id_instance]->label = $this->title;
280 }
281
282 return $rates;
283 }
284
285 /**
286 * Title translation override for WPML (woocommerce_order_get_items)
287 *
288 * @since 3.0.0
289 */
290 public function override_shipping_title_translation( $items, $order ) {
291
292 foreach ( $items as $key => $item ) {
293
294 if ( $item['type'] == 'shipping' ) {
295 foreach ( $item['item_meta_array'] as $meta ) {
296
297 if ( $meta->key === 'method_id' ) {
298
299 $tmp = explode( ':', $meta->value );
300
301 if ( $tmp[0] == $this->id ) {
302 $items[$key]['name'] = $this->title;
303 }
304 }
305 }
306 }
307 }
308
309 return $items;
310 }
311
312 /**
313 * Sets default settings used by all shipping methods
314 *
315 * @since 3.0.0
316 */
317 final public function set_default_settings() {
318
319 $this->enable = !empty( $this->settings['active'] ) ? $this->settings['active'] : null;
320 $this->availability = 'specific';
321 $this->order_country = 'unknown';
322 $this->maximum_weight = !empty( $this->settings['maximum_weight']) ? ( double )$this->settings['maximum_weight'] : 0;
323 $this->free_shipping_min_amount = !empty( $this->settings['free_shipping_min_amount'] ) ? $this->settings['free_shipping_min_amount'] : 0;
324 $this->countries = !empty( $this->settings['countries'] ) ? $this->settings['countries'] : array();
325
326 //Overrides title if its set in settings already
327 $this->override_title();
328 }
329
330 /**
331 * Check if the shipping method is available (automatically called by parent \WC_Shipping_Method)
332 *
333 * @since 3.0.0
334 */
335 public function is_available( $package ) {
336
337 //check if cart weight exceeds max weight
338 if ( WC()->cart->cart_contents_weight > $this->maximum_weight ) {
339 return false;
340 }
341
342 $is_available = $this->enable === 'yes';
343
344 if ( !$is_available || !\MakeCommerce::is_api_set() ) {
345 return false;
346 }
347
348 $this->order_country = $package['destination']['country'];
349
350 return apply_filters( 'woocommerce_shipping_' . $this->id . '_is_available', $is_available, $package );
351 }
352
353 /**
354 * Calculate price for shipping
355 *
356 * @since 3.0.0
357 */
358 public function calculate_shipping( $package = array() ) {
359
360 $price = isset( $this->instance_settings['price'] ) ? $this->instance_settings['price'] : ( isset( $this->settings['price_'.$package['destination']['country']] ) ? $this->settings['price_'.$package['destination']['country']] : 0 );
361
362 $rate = array(
363 'id' => $this->get_rate_id(),
364 'label' => $this->title,
365 'cost' => $price,
366 'package' => $package,
367 'calc_tax' => 'per_order',
368 );
369
370 //check shippingclasses
371 $rate = $this->calculate_shipping_class_price( $rate, $package );
372
373 //check all free shipping options:
374 $free_shipping_min_amount = $this->instance_settings['free_shipping_min_amount'];
375
376 if ( $free_shipping_min_amount && $package['contents_cost'] >= $free_shipping_min_amount ) {
377 $rate["cost"] = 0;
378 }
379
380 //check only for parcelmachines
381 if ( $this->type == "apt" ) {
382
383 $free_shipping = true;
384 foreach ( $package['contents'] as $line ) {
385
386 $free_shipping = get_post_meta( $line['product_id'], '_no_shipping_cost', true ) === 'yes' ? $free_shipping : false;
387 }
388
389 if ( $free_shipping ) {
390 $rate["cost"] = 0;
391 }
392 }
393
394 //check if there is a free shipping coupon (if it's free then allow free shipping)
395 if ( isset( $package['applied_coupons'] ) && $this->instance_settings["allow_free_shipping_coupons"] === "yes" ) {
396 foreach ( $package['applied_coupons'] as $coupon_code ) {
397
398 $coupon = new \WC_Coupon( $coupon_code );
399 if ( $coupon->get_free_shipping() === true ) {
400 $rate["cost"] = 0;
401 break;
402 }
403 }
404 }
405
406 $this->add_rate( $rate );
407 }
408
409 /**
410 * This function overrides shipping method title
411 * if you have set language specific titles in admin.
412 *
413 * @since 3.0.0
414 */
415 final public function override_title() {
416
417 $language_code = \MakeCommerce\i18n::get_two_char_locale();
418
419 //default (no language)
420 if ( !empty( $this->settings['method_name'] ) ) {
421 $this->title = $this->settings['method_name'];
422 }
423
424 //if method name setting has been set with current language code
425 if ( !empty( $this->settings['method_name_' . $language_code] ) ) {
426 $this->title = $this->settings['method_name_' . $language_code];
427 }
428 }
429
430 /**
431 * Initializes basic fields used by all shipping methods.
432 *
433 * @since 3.0.0
434 */
435 public function initialize_form_fields() {
436
437 //Initialize instance form fields first
438 $this->initialize_instance_form_fields();
439
440 //Initialize basic form fields
441 $this->initialize_basic_form_fields();
442
443 //Initialize method specific form fields
444 $this->initialize_method_type_form_fields();
445
446 //Initialize return address fields
447 $this->initialize_return_address_form_fields();
448 }
449
450 /**
451 * Initializes basic shared form fields
452 * Used by all shipping methods
453 *
454 * @since 3.0.0
455 */
456 final public function initialize_basic_form_fields() {
457
458 $this->form_fields = array();
459
460 $this->form_fields['logo'] = array(
461 'type' => 'title',
462 'description' => \MakeCommerce::get_logo_html()
463 );
464
465 $this->form_fields['generic'] = array(
466 'type' => 'title',
467 'title' => __( 'Generic and pricing options', 'wc_makecommerce_domain' ),
468 );
469
470 $this->form_fields['active'] = array(
471 'title' => __( 'Enable', 'wc_makecommerce_domain' ),
472 'type' => 'checkbox',
473 'label' => __( 'enabled', 'wc_makecommerce_domain' ),
474 'default' => 'no',
475 );
476
477 $this->form_fields['maximum_weight'] = array(
478 'title' => sprintf( __( 'Maximum weight allowed for shipping (%s)', 'wc_makecommerce_domain' ), get_option( 'woocommerce_weight_unit' ) ),
479 'type' => 'text',
480 'default' => $this->default_max_weight
481 );
482
483 $this->form_fields['look_and_feel'] = array(
484 'type' => 'title',
485 'title' => '<hr><br>'.__( 'Look and feel options', 'wc_makecommerce_domain' ),
486 'description' => __( 'Options for presentation on check-out page', 'wc_makecommerce_domain' )
487 );
488
489 //get a list of all active languages
490 $languages = \MakeCommerce\i18n::get_active_languages();
491
492 if ( empty( $languages ) ) {
493
494 $this->form_fields['method_name'] = array(
495 'title' => __( 'Shipping Method Title', 'wc_makecommerce_domain' ),
496 'type' => 'text',
497 'default' => $this->carrier_title . " " . \MakeCommerce\i18n::get_string_from_mo( $this->identifier, 'wc_makecommerce_domain', \MakeCommerce\i18n::get_two_char_locale() )
498 );
499 } else {
500 foreach ( $languages as $language_code => $language ) {
501 $this->form_fields['method_name_'.substr( $language_code, 0, 2 )] = array(
502 'title' => __('Shipping Method Title', 'wc_makecommerce_domain').sprintf(' (%s)', substr($language_code, 0, 2)),
503 'type' => 'text',
504 'default' => $this->carrier_title . " " . \MakeCommerce\i18n::get_string_from_mo( $this->identifier, 'wc_makecommerce_domain', substr( $language_code, 0, 2 ) )
505 );
506 }
507 }
508
509 $this->initialize_method_type_checkout_fields();
510
511 if ( \MakeCommerce::new_woo_version() ) {
512 $a = 'admin.php?page=wc-settings&tab=advanced&section=mk_api';
513 } else {
514 $a = 'admin.php?page=wc-settings&tab=api&section=mk_api';
515 }
516
517 $this->form_fields['api_access'] = array(
518 'type' => 'title',
519 'title' => '<hr><br>'.__( 'API access for', 'wc_makecommerce_domain' ).' '.$this->carrier,
520 'description' => sprintf(__('You can automatically create shipments into %s system and print the out the package labels right here, at the shop orders view. <br> Please set your %s web services account credentials below here. <br>(see more on <a href="https://makecommerce.net/en/integration-modules/makecommerce-woocommerce-payment-plugin/#carriers-integration">MakeCommerce plugin page</a>. Don\'t forget to enable also <a href="%s">MC API keys</a>!)', 'wc_makecommerce_domain' ), $this->carrier, $this->carrier, admin_url( $a ) )
521 );
522 }
523
524 /**
525 * Initializes basic instance fields used by all shipping methods.
526 * Automatically called by \WC_Shipping_Method
527 *
528 * @since 3.0.0
529 */
530 final public function initialize_instance_form_fields() {
531
532 $this->instance_form_fields = array();
533
534 $this->instance_form_fields['logo'] = array(
535 'type' => 'title',
536 'description' => \MakeCommerce::get_logo_html()
537 );
538
539 $this->instance_form_fields['price'] = array(
540 'title' => __('Shipping price', 'wc_makecommerce_domain'),
541 'type' => 'text',
542 'default' => $this->default_price
543 );
544
545 $this->instance_form_fields['free_shipping_min_amount'] = array(
546 'title' => __('Free shipping amount', 'wc_makecommerce_domain'),
547 'type' => 'number',
548 'default' => '0',
549 'desc_tip' => __( '(0 means no free shipping)', 'wc_makecommerce_domain' ),
550 );
551
552 $this->instance_form_fields['allow_free_shipping_coupons'] = array(
553 'title' => __( 'Free shipping coupons', 'wc_makecommerce_domain' ),
554 'type' => 'checkbox',
555 'default' => 'no',
556 'desc_tip' => __( 'Allow using free shipping coupons to be used with this method', 'wc_makecommerce_domain' ),
557 );
558
559 $this->instance_form_fields = $this->add_form_fields( $this->instance_form_fields );
560 }
561
562 /**
563 * Initializes return address form fields
564 * Used by all shipping methods
565 *
566 * @since 3.0.0
567 */
568 public function initialize_return_address_form_fields() {
569
570 $this->form_fields['return_address'] = array(
571 'type' => 'title',
572 'title' => __( 'Return address', 'wc_makecommerce_domain' ),
573 'description' => sprintf( __( 'Please define return address for %s shipments.<br><b>All fields are required.</b>', 'wc_makecommerce_domain' ), $this->carrier )
574 );
575
576 $this->form_fields['shop_name'] = array(
577 'type' => 'text',
578 'title' => __( 'Shop name', 'wc_makecommerce_domain' ),
579 'class' => 'input-text regular-input',
580 );
581
582 $this->form_fields['shop_phone'] = array(
583 'type' => 'text',
584 'title' => __( 'Shop phone (mobile)', 'wc_makecommerce_domain' ),
585 'class' => 'input-text regular-input',
586 );
587
588 $this->form_fields['shop_email'] = array(
589 'type' => 'text',
590 'title' => __( 'Shop email', 'wc_makecommerce_domain' ),
591 'class' => 'input-text regular-input',
592 );
593
594 $this->form_fields['shop_address_country'] = array(
595 'type' => 'select',
596 'title' => __( 'Shop address country', 'wc_makecommerce_domain' ),
597 'options' => array(
598 'EE' => 'EE',
599 'LV' => 'LV',
600 'LT' => 'LT',
601 ),
602 'default' => 'EE',
603 );
604
605 $this->form_fields['shop_address_city'] = array(
606 'type' => 'text',
607 'title' => __( 'Shop address city', 'wc_makecommerce_domain' ),
608 'class' => 'input-text regular-input',
609 );
610
611 $this->form_fields['shop_postal_code'] = array(
612 'type' => 'text',
613 'title' => __( 'Shop postal code', 'wc_makecommerce_domain' ),
614 'class' => 'input-text regular-input',
615 );
616
617 $this->form_fields['shop_address_street'] = array(
618 'type' => 'text',
619 'title' => __( 'Shop address street', 'wc_makecommerce_domain' ),
620 'class' => 'input-text regular-input',
621 );
622 }
623
624 /**
625 * Initializes method type specific form fields
626 * Loads method specific fields
627 *
628 * @since 3.0.0
629 */
630 public function initialize_method_type_form_fields() {
631
632 //Initialize method specific fields
633 $this->initialize_method_form_fields();
634
635 $this->form_fields['service_user'] = array(
636 'title' => sprintf( __( '%s web services username', 'wc_makecommerce_domain' ), $this->service_name ),
637 'type' => 'text',
638 'default' => ''
639 );
640
641 $this->form_fields['service_password'] = array(
642 'title' => sprintf( __( '%s web services password', 'wc_makecommerce_domain' ), $this->service_name ),
643 'type' => 'text',
644 'default' => ''
645 );
646 }
647
648 /**
649 * Initializes method type, required function for all method types
650 *
651 * @since 3.0.0
652 */
653 abstract public function initialize();
654
655 /**
656 * Sets title of the method
657 *
658 * @since 3.0.0
659 */
660 abstract public function return_method_title();
661
662 /**
663 * Returns phone validation error
664 *
665 * @since 3.0.4
666 */
667 abstract public function phone_number_validation_error();
668 }
669