PluginProbe
WooCommerce Tax (formerly WooCommerce Shipping & Tax) / 3.6.7
WooCommerce Tax (formerly WooCommerce Shipping & Tax) v3.6.7
3.6.15 3.6.14 3.6.13 3.6.12 3.6.11 1.4.0 1.4.1 1.5.0 1.6.0 1.6.1 1.6.2 1.7.0 1.7.1 1.8.0 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 2.0.0 2.1.0 2.1.1 2.2.0 2.2.1 2.2.2 All 175 releases
woocommerce-services / woocommerce-services.php

woocommerce-services.php in WooCommerce Tax (formerly WooCommerce Shipping & Tax) 3.6.7, at woocommerce-services.php

2,271 lines 82.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: WooCommerce Tax
4 * Requires Plugins: woocommerce
5 * Plugin URI: https://woocommerce.com/
6 * Description: Automated tax calculation for WooCommerce.
7 * Author: WooCommerce
8 * Author URI: https://woocommerce.com/
9 * Text Domain: woocommerce-services
10 * Domain Path: /i18n/languages/
11 * Version: 3.6.7
12 * Requires Plugins: woocommerce
13 * Requires PHP: 7.4
14 * Requires at least: 6.9
15 * Tested up to: 7.0
16 * WC requires at least: 10.7
17 * WC tested up to: 10.9
18 *
19 * Copyright (c) 2017-2023 Automattic
20 *
21 * This program is free software: you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation, either version 3 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program. If not, see <http://www.gnu.org/licenses/>.
33 *
34 * WooCommerce Tax incorporates code from WooCommerce Sales Tax Plugin by TaxJar, Copyright 2014-2017 TaxJar.
35 * WooCommerce Sales Tax Plugin by TaxJar is distributed under the terms of the GNU GPL, Version 2 (or later).
36 */
37
38 if ( ! defined( 'ABSPATH' ) ) {
39 exit;
40 }
41
42 require_once __DIR__ . '/vendor/autoload_packages.php';
43
44 require_once __DIR__ . '/classes/class-wc-connect-extension-compatibility.php';
45 require_once __DIR__ . '/classes/class-wc-connect-functions.php';
46 require_once __DIR__ . '/classes/class-wc-connect-jetpack.php';
47 require_once __DIR__ . '/classes/class-wc-connect-options.php';
48
49 use Automattic\WCServices\StoreNotices\StoreNoticesController;
50 use Automattic\WCServices\StoreNotices\StoreNoticesNotifier;
51 use Automattic\WCServices\Integrations\WooCommerceBlocksIntegration;
52 use Automattic\WCServices\StoreApi\Extensions\StoreNoticesExtension;
53 use Automattic\WCServices\StoreApi\StoreApiExtendSchema;
54 use Automattic\WCServices\StoreApi\StoreApiExtensionController;
55 use Automattic\WooCommerce\Utilities\OrderUtil;
56
57 if ( ! class_exists( 'WC_Connect_Loader' ) ) {
58 define( 'WOOCOMMERCE_CONNECT_MINIMUM_WOOCOMMERCE_VERSION', '2.6' );
59 define( 'WOOCOMMERCE_CONNECT_MAX_JSON_DECODE_DEPTH', 32 );
60
61 if ( ! defined( 'WOOCOMMERCE_CONNECT_SERVER_API_VERSION' ) ) {
62 define( 'WOOCOMMERCE_CONNECT_SERVER_API_VERSION', '5' );
63 }
64
65 define( 'WCSERVICES_PLUGIN_FILE', __FILE__ );
66 define( 'WCSERVICES_PLUGIN_DIR', __DIR__ );
67 define( 'WCSERVICES_PLUGIN_DIST_DIR', WCSERVICES_PLUGIN_DIR . '/dist/' );
68 define( 'WCSERVICES_PLUGIN_URL', plugin_dir_url( WCSERVICES_PLUGIN_FILE ) );
69 define( 'WCSERVICES_PLUGIN_DIST_URL', plugin_dir_url( WCSERVICES_PLUGIN_FILE ) . 'dist/' );
70 define( 'WCSERVICES_ASSETS_URL', WCSERVICES_PLUGIN_URL . 'assets/' );
71 define( 'WCSERVICES_STYLESHEETS_URL', WCSERVICES_ASSETS_URL . 'stylesheets/' );
72 define( 'WCSERVICES_ASSETS_DIR', WCSERVICES_PLUGIN_DIR . '/assets/' );
73 define( 'WCSERVICES_STYLESHEETS_DIR', WCSERVICES_ASSETS_DIR . 'stylesheets/' );
74
75 class WC_Connect_Loader {
76
77 /**
78 * @var WC_Connect_Logger
79 */
80 protected $logger;
81
82 /**
83 * @var WC_Connect_Logger
84 */
85 protected $shipping_logger;
86
87 /**
88 * @var WC_Connect_API_Client
89 */
90 protected $api_client;
91
92 /**
93 * @var WC_Connect_Service_Schemas_Store
94 */
95 protected $service_schemas_store;
96
97 /**
98 * @var WC_Connect_Service_Settings_Store
99 */
100 protected $service_settings_store;
101
102 /**
103 * @var WC_Connect_Payment_Methods_Store
104 */
105 protected $payment_methods_store;
106
107 /**
108 * @var WC_REST_Connect_Account_Settings_Controller
109 */
110 protected $rest_account_settings_controller;
111
112 /**
113 * @var WC_REST_Connect_Packages_Controller
114 */
115 protected $rest_packages_controller;
116
117 /**
118 * @var WC_REST_Connect_Services_Controller
119 */
120 protected $rest_services_controller;
121
122 /**
123 * @var WC_REST_Connect_Self_Help_Controller
124 */
125 protected $rest_self_help_controller;
126
127 /**
128 * @var WC_REST_Connect_Shipping_Label_Controller
129 */
130 protected $rest_shipping_label_controller;
131
132 /**
133 * @var WC_REST_Connect_Shipping_Label_Status_Controller
134 */
135 protected $rest_shipping_label_status_controller;
136
137 /**
138 * @var WC_REST_Connect_Shipping_Label_Refund_Controller
139 */
140 protected $rest_shipping_label_refund_controller;
141
142 /**
143 * @var WC_REST_Connect_Shipping_Label_Preview_Controller
144 */
145 protected $rest_shipping_label_preview_controller;
146
147 /**
148 * @var WC_REST_Connect_Shipping_Label_Print_Controller
149 */
150 protected $rest_shipping_label_print_controller;
151
152 /**
153 * @var WC_REST_Connect_Shipping_Rates_Controller
154 */
155 protected $rest_shipping_rates_controller;
156
157 /**
158 * @var WC_REST_Connect_Address_Normalization_Controller
159 */
160 protected $rest_address_normalization_controller;
161
162 /**
163 *
164 * WC_REST_Connect_Shipping_Carrier_Types_Controller
165 *
166 * @var WC_REST_Connect_Shipping_Carrier_Types_Controller
167 */
168 protected $rest_carrier_types_controller;
169
170 /**
171 * WC_REST_Connect_Assets_Controller
172 *
173 * @var WC_REST_Connect_Assets_Controller
174 */
175 protected $rest_assets_controller;
176
177 /**
178 * WC_REST_Connect_Shipping_Carrier_Controller
179 *
180 * @var WC_REST_Connect_Shipping_Carrier_Controller
181 */
182 protected $rest_carrier_controller;
183
184 /**
185 * @var StoreNoticesNotifier
186 */
187 protected $store_notices_notifier;
188
189 /**
190 * WC_REST_Connect_Shipping_Carriers_Controller
191 *
192 * @var WC_REST_Connect_Shipping_Carriers_Controller
193 */
194 protected $rest_carriers_controller;
195
196 /**
197 * WC_REST_Connect_Subscriptions_Controller
198 *
199 * @var WC_REST_Connect_Subscriptions_Controller
200 */
201 protected $rest_subscriptions_controller;
202
203 /**
204 * WC_REST_Connect_Subscription_Activate_Controller
205 *
206 * @var WC_REST_Connect_Subscription_Activate_Controller
207 */
208 protected $rest_subscription_activate_controller;
209
210 /**
211 * WC_REST_Connect_Shipping_Carrier_Delete_Controller
212 *
213 * @var WC_REST_Connect_Shipping_Carrier_Delete_Controller
214 */
215 protected $rest_carrier_delete_controller;
216
217 /**
218 * WC_REST_Connect_Migration_Flag_Controller
219 *
220 * @var WC_REST_Connect_Migration_Flag_Controller
221 */
222 protected $rest_migration_flag_controller;
223
224 /**
225 * @var WC_Connect_Service_Schemas_Validator
226 */
227 protected $service_schemas_validator;
228
229 /**
230 * @var WC_Connect_Settings_Pages
231 */
232 protected $settings_pages;
233
234 /**
235 * @var WC_Connect_Help_View
236 */
237 protected $help_view;
238
239 /**
240 * @var WC_Connect_Shipping_Label
241 */
242 protected $shipping_label;
243
244 /**
245 * @var WC_Connect_Nux
246 */
247 protected $nux;
248
249 /**
250 * @var WC_Connect_TaxJar_Integration
251 */
252 protected $taxjar;
253
254 /**
255 * @var WC_Connect_PayPal_EC
256 */
257 protected $paypal_ec;
258
259 /**
260 * @var WC_Connect_Tracks
261 */
262 protected $tracks;
263
264 /**
265 * @var WC_Connect_Label_Reports
266 */
267 protected $label_reports;
268
269 /**
270 * @var WC_REST_Connect_Tos_Controller
271 */
272 protected $rest_tos_controller;
273
274 protected $services = array();
275
276 protected $service_object_cache = array();
277
278 protected $wc_connect_base_url;
279
280 protected static $wcs_version;
281
282 public const MIGRATION_DISMISSAL_COOKIE_KEY = 'wcst-wcshipping-migration-dismissed';
283 private const SIFT_FETCH_IN_PROGRESS_TRANSIENT_KEY = 'wc_connect_sift_fetch_in_progress';
284
285 public static function plugin_deactivation() {
286 wp_clear_scheduled_hook( 'wc_connect_fetch_service_schemas' );
287
288 /*
289 * When we deactivate the plugin after wcshipping_migration_state has started,
290 * that means the migration is done. We can mark it as completed before we deactivate the plugin.
291 */
292 require_once __DIR__ . '/classes/class-wc-connect-logger.php';
293 require_once __DIR__ . '/classes/class-wc-connect-tracks.php';
294 require_once __DIR__ . '/classes/class-wc-connect-wcst-to-wcshipping-migration-state-enum.php';
295
296 $migration_state = intval( get_option( 'wcshipping_migration_state' ) );
297 if ( $migration_state === WC_Connect_WCST_To_WCShipping_Migration_State_Enum::COMPLETED ) {
298 $core_logger = new WC_Logger();
299 $logger = new WC_Connect_Logger( $core_logger );
300 $tracks = new WC_Connect_Tracks( $logger, __FILE__ );
301 $tracks->record_user_event(
302 'migration_flag_state_update',
303 array(
304 'migration_state' => $migration_state,
305 'updated' => false,
306 )
307 );
308 }
309 }
310
311 public static function plugin_uninstall() {
312 WC_Connect_Options::delete_all_options();
313 self::delete_notices();
314 }
315
316 /**
317 * Deletes WC Admin notices.
318 */
319 public static function delete_notices() {
320 if ( self::can_add_wc_admin_notice() ) {
321 require_once __DIR__ . '/classes/class-wc-connect-note-dhl-live-rates-available.php';
322 WC_Connect_Note_DHL_Live_Rates_Available::possibly_delete_note();
323 }
324 }
325 /**
326 * Checks if WC Admin is active and includes needed classes.
327 *
328 * @return bool true|false.
329 */
330 public static function can_add_wc_admin_notice() {
331 if ( ! class_exists( 'WC_Data_Store' ) ) {
332 return false;
333 }
334
335 try {
336 WC_Data_Store::load( 'admin-note' );
337 } catch ( Exception $e ) {
338 return false;
339 }
340
341 return trait_exists( 'Automattic\WooCommerce\Admin\Notes\NoteTraits' ) && class_exists( 'Automattic\WooCommerce\Admin\Notes\Note' );
342 }
343
344 /**
345 * Get WCS plugin version
346 *
347 * @return string
348 */
349 public static function get_wcs_version() {
350 if ( null === self::$wcs_version ) {
351 $plugin_data = get_file_data( __FILE__, array( 'Version' => 'Version' ) );
352 self::$wcs_version = $plugin_data['Version'];
353 }
354 return self::$wcs_version;
355 }
356
357 /**
358 * Get base url.
359 *
360 * @return string
361 */
362 private static function get_wc_connect_base_url() {
363 return trailingslashit( defined( 'WOOCOMMERCE_CONNECT_DEV_SERVER_URL' ) ? WOOCOMMERCE_CONNECT_DEV_SERVER_URL : plugins_url( 'dist/', __FILE__ ) );
364 }
365
366 /**
367 * Get WCS admin script url.
368 *
369 * @return string
370 */
371 public static function get_wcs_admin_script_url() {
372 return self::get_wc_connect_base_url() . 'woocommerce-services-' . self::get_wcs_version() . '.js';
373 }
374
375 /**
376 * Get WCS admin css url.
377 *
378 * @return string
379 */
380 public static function get_wcs_admin_style_url() {
381 if ( ! defined( 'WOOCOMMERCE_CONNECT_DEV_SERVER_URL' ) ) {
382 return self::get_wc_connect_base_url() . 'woocommerce-services-' . self::get_wcs_version() . '.css';
383 } else {
384 return '';
385 }
386 }
387
388 public function __construct() {
389 $this->wc_connect_base_url = self::get_wc_connect_base_url();
390 add_action(
391 'before_woocommerce_init',
392 function () {
393 if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) {
394 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', 'woocommerce-services/woocommerce-services.php' );
395 }
396 }
397 );
398
399 add_action( 'plugins_loaded', array( $this, 'on_plugins_loaded' ) );
400 add_action( 'after_setup_theme', array( $this, 'ensure_jetpack_connection' ), 1 );
401
402 /**
403 * Used to let WC Tax know WCS&T will handle the plugin's coexistence.
404 *
405 * WCS&T does it by not registering its functionality and displaying an appropriate notice
406 * in WP admin.
407 *
408 * The filter also includes "woo_shipping" in its name, but we've since implemented conditional
409 * loading of all shipping functionality inside WCS&T, so there's no special need for any
410 * "parallel support" being done in WC Shipping anymore.
411 *
412 * This new feature is represented via the "wc_services_will_disable_shipping_logic" hook.
413 */
414 if ( $this->are_woo_shipping_and_woo_tax_active() ) {
415 add_filter( 'wc_services_will_handle_coexistence_with_woo_shipping_and_woo_tax', '__return_true' );
416 }
417
418 // Let WC Shipping know that the current version of WCS&T supports conditional shipping logic loading.
419 add_filter( 'wc_services_will_disable_shipping_logic', '__return_true' );
420 }
421
422 public function get_logger() {
423 return $this->logger;
424 }
425
426 public function set_logger( WC_Connect_Logger $logger ) {
427 $this->logger = $logger;
428 }
429
430 public function get_shipping_logger() {
431 return $this->shipping_logger;
432 }
433
434 public function set_shipping_logger( WC_Connect_Logger $logger ) {
435 $this->shipping_logger = $logger;
436 }
437
438 public function get_api_client() {
439 return $this->api_client;
440 }
441
442 public function set_api_client( WC_Connect_API_Client $api_client ) {
443 $this->api_client = $api_client;
444 }
445
446 public function get_service_schemas_store() {
447 return $this->service_schemas_store;
448 }
449
450 public function set_service_schemas_store( WC_Connect_Service_Schemas_Store $schemas_store ) {
451 $this->service_schemas_store = $schemas_store;
452 }
453
454 public function get_service_settings_store() {
455 return $this->service_settings_store;
456 }
457
458 public function set_service_settings_store( WC_Connect_Service_Settings_Store $settings_store ) {
459 $this->service_settings_store = $settings_store;
460 }
461
462 public function get_payment_methods_store() {
463 return $this->payment_methods_store;
464 }
465
466 public function set_payment_methods_store( WC_Connect_Payment_Methods_Store $payment_methods_store ) {
467 $this->payment_methods_store = $payment_methods_store;
468 }
469
470 public function get_tracks() {
471 return $this->tracks;
472 }
473
474 public function set_tracks( WC_Connect_Tracks $tracks ) {
475 $this->tracks = $tracks;
476 }
477
478 public function get_rest_account_settings_controller() {
479 return $this->rest_account_settings_controller;
480 }
481
482 public function set_rest_tos_controller( WC_REST_Connect_Tos_Controller $rest_tos_controller ) {
483 $this->rest_tos_controller = $rest_tos_controller;
484 }
485
486 public function set_rest_assets_controller( WC_REST_Connect_Assets_Controller $rest_assets_controller ) {
487 $this->rest_assets_controller = $rest_assets_controller;
488 }
489
490 public function set_rest_carriers_controller( WC_REST_Connect_Shipping_Carriers_Controller $rest_carriers_controller ) {
491 $this->rest_carriers_controller = $rest_carriers_controller;
492 }
493
494 public function set_rest_subscriptions_controller( WC_REST_Connect_Subscriptions_Controller $rest_subscriptions_controller ) {
495 $this->rest_subscriptions_controller = $rest_subscriptions_controller;
496 }
497
498 public function set_rest_subscription_activate_controller( WC_REST_Connect_Subscription_Activate_Controller $rest_subscription_activate_controller ) {
499 $this->rest_subscription_activate_controller = $rest_subscription_activate_controller;
500 }
501
502 public function set_rest_carrier_controller( WC_REST_Connect_Shipping_Carrier_Controller $rest_carrier_controller ) {
503 $this->rest_carrier_controller = $rest_carrier_controller;
504 }
505
506 public function set_rest_carrier_delete_controller( WC_REST_Connect_Shipping_Carrier_Delete_Controller $rest_carrier_delete_controller ) {
507 $this->rest_carrier_delete_controller = $rest_carrier_delete_controller;
508 }
509
510 public function set_rest_packages_controller( WC_REST_Connect_Packages_Controller $rest_packages_controller ) {
511 $this->rest_packages_controller = $rest_packages_controller;
512 }
513
514 public function set_rest_account_settings_controller( WC_REST_Connect_Account_Settings_Controller $rest_account_settings_controller ) {
515 $this->rest_account_settings_controller = $rest_account_settings_controller;
516 }
517
518 public function get_rest_services_controller() {
519 return $this->rest_services_controller;
520 }
521
522 public function set_rest_services_controller( WC_REST_Connect_Services_Controller $rest_services_controller ) {
523 $this->rest_services_controller = $rest_services_controller;
524 }
525
526 public function get_rest_self_help_controller() {
527 return $this->rest_self_help_controller;
528 }
529
530 public function set_rest_self_help_controller( WC_REST_Connect_Self_Help_Controller $rest_self_help_controller ) {
531 $this->rest_self_help_controller = $rest_self_help_controller;
532 }
533
534 public function get_rest_shipping_label_controller() {
535 return $this->rest_shipping_label_controller;
536 }
537
538 public function set_rest_shipping_label_controller( WC_REST_Connect_Shipping_Label_Controller $rest_shipping_label_controller ) {
539 $this->rest_shipping_label_controller = $rest_shipping_label_controller;
540 }
541
542 public function get_rest_shipping_label_status_controller() {
543 return $this->rest_shipping_label_status_controller;
544 }
545
546 public function set_rest_shipping_label_status_controller( WC_REST_Connect_Shipping_Label_Status_Controller $rest_shipping_label_status_controller ) {
547 $this->rest_shipping_label_status_controller = $rest_shipping_label_status_controller;
548 }
549
550 public function get_rest_shipping_label_refund_controller() {
551 return $this->rest_shipping_label_refund_controller;
552 }
553
554 public function set_rest_shipping_label_refund_controller( WC_REST_Connect_Shipping_Label_Refund_Controller $rest_shipping_label_refund_controller ) {
555 $this->rest_shipping_label_refund_controller = $rest_shipping_label_refund_controller;
556 }
557
558 public function get_rest_shipping_label_preview_controller() {
559 return $this->rest_shipping_label_preview_controller;
560 }
561
562 public function set_rest_shipping_label_preview_controller( WC_REST_Connect_Shipping_Label_Preview_Controller $rest_shipping_label_preview_controller ) {
563 $this->rest_shipping_label_preview_controller = $rest_shipping_label_preview_controller;
564 }
565
566 public function get_rest_shipping_label_print_controller() {
567 return $this->rest_shipping_label_print_controller;
568 }
569
570 public function set_rest_shipping_label_print_controller( WC_REST_Connect_Shipping_Label_Print_Controller $rest_shipping_label_print_controller ) {
571 $this->rest_shipping_label_print_controller = $rest_shipping_label_print_controller;
572 }
573
574 public function set_rest_shipping_rates_controller( WC_REST_Connect_Shipping_Rates_Controller $rest_shipping_rates_controller ) {
575 $this->rest_shipping_rates_controller = $rest_shipping_rates_controller;
576 }
577
578 public function set_rest_address_normalization_controller( WC_REST_Connect_Address_Normalization_Controller $rest_address_normalization_controller ) {
579 $this->rest_address_normalization_controller = $rest_address_normalization_controller;
580 }
581
582 public function set_carrier_types_controller( WC_REST_Connect_Shipping_Carrier_Types_Controller $rest_carrier_types_controller ) {
583 $this->rest_carrier_types_controller = $rest_carrier_types_controller;
584 }
585
586 public function set_rest_migration_flag_controller( WC_REST_Connect_Migration_Flag_Controller $rest_migration_flag_controller ) {
587 $this->rest_migration_flag_controller = $rest_migration_flag_controller;
588 }
589
590 public function get_carrier_types_controller() {
591 return $this->rest_carrier_types_controller;
592 }
593
594 public function get_service_schemas_validator() {
595 return $this->service_schemas_validator;
596 }
597
598 public function set_service_schemas_validator( WC_Connect_Service_Schemas_Validator $validator ) {
599 $this->service_schemas_validator = $validator;
600 }
601
602 public function get_settings_pages() {
603 return $this->settings_pages;
604 }
605
606 public function set_settings_pages( WC_Connect_Settings_Pages $settings_pages ) {
607 $this->settings_pages = $settings_pages;
608 }
609
610 public function get_help_view() {
611 return $this->help_view;
612 }
613
614 public function set_help_view( WC_Connect_Help_View $help_view ) {
615 $this->help_view = $help_view;
616 }
617
618 public function set_shipping_label( WC_Connect_Shipping_Label $shipping_label ) {
619 $this->shipping_label = $shipping_label;
620 }
621
622 public function set_nux( WC_Connect_Nux $nux ) {
623 $this->nux = $nux;
624 }
625
626 public function set_taxjar( WC_Connect_TaxJar_Integration $taxjar ) {
627 $this->taxjar = $taxjar;
628 }
629
630 public function set_paypal_ec( WC_Connect_PayPal_EC $paypal_ec ) {
631 $this->paypal_ec = $paypal_ec;
632 }
633
634 public function set_label_reports( WC_Connect_Label_Reports $label_reports ) {
635 $this->label_reports = $label_reports;
636 }
637
638 /**
639 * @return StoreNoticesNotifier
640 */
641 public function get_store_notices_notifier() {
642 return $this->store_notices_notifier;
643 }
644
645 /**
646 * @param StoreNoticesNotifier $store_notices_notifier
647 */
648 public function set_store_notices_notifier( StoreNoticesNotifier $store_notices_notifier ) {
649 $this->store_notices_notifier = $store_notices_notifier;
650 }
651
652 /**
653 * Load our textdomain
654 *
655 * @codeCoverageIgnore
656 */
657 public function load_textdomain() {
658 load_plugin_textdomain( 'woocommerce-services', false, dirname( plugin_basename( __FILE__ ) ) . '/i18n/languages' );
659 }
660
661 public function ensure_jetpack_connection() {
662 $jetpack_config = new Automattic\Jetpack\Config();
663 $jetpack_config->ensure(
664 'connection',
665 array(
666 'slug' => WC_Connect_Jetpack::JETPACK_PLUGIN_SLUG,
667 'name' => $this->get_plugin_name_for_new_sites(),
668 )
669 );
670 }
671
672 public function on_plugins_loaded() {
673
674 /**
675 * Allow third party logic to determine if this plugin should initiate its logic.
676 *
677 * The primary purpose here is to allow a smooth transition between the new WC Tax plugin
678 * and WooCommerce Tax (this plugin), by letting them take over all responsibilities if all three
679 * plugins are activated at the same time.
680 *
681 * @since {{next-release}}
682 *
683 * @param bool $status The value will determine if we should initiate the plugins logic or not.
684 */
685 if ( apply_filters( 'wc_services_will_handle_coexistence_with_woo_shipping_and_woo_tax', false ) ) {
686 // Show message that WCS&T is no longer doing anything.
687 add_action( 'admin_notices', array( $this, 'display_woo_shipping_and_woo_tax_are_active_notice' ) );
688
689 // Bail, so none of our tax and shipping logic will be initiated.
690 return;
691 }
692
693 if ( ! class_exists( 'WooCommerce' ) ) {
694 add_action(
695 'admin_notices',
696 function () {
697 /* translators: %s WC download URL link. */
698 echo '<div class="error"><p><strong>' . sprintf( esc_html__( 'WooCommerce Tax requires the WooCommerce plugin to be installed and active. You can download %s here.', 'woocommerce-services' ), '<a href="https://wordpress.org/plugins/woocommerce/" target="_blank">WooCommerce</a>' ) . '</strong></p></div>';
699 }
700 );
701 return;
702 }
703
704 add_action( 'woocommerce_blocks_loaded', array( $this, 'register_blocks_integration' ) );
705 add_action( 'before_woocommerce_init', array( $this, 'pre_wc_init' ) );
706 }
707
708 /**
709 * Register the WooCommerceBlocks integration.
710 */
711 public function register_blocks_integration() {
712 add_action(
713 'woocommerce_blocks_checkout_block_registration',
714 function ( $integration_registry ) {
715 $integration_registry->register( new WooCommerceBlocksIntegration( $this->wc_connect_base_url ) );
716 }
717 );
718 add_action(
719 'woocommerce_blocks_cart_block_registration',
720 function ( $integration_registry ) {
721 $integration_registry->register( new WooCommerceBlocksIntegration( $this->wc_connect_base_url ) );
722 }
723 );
724 }
725
726 /**
727 * Perform plugin bootstrapping that needs to happen before WC init.
728 *
729 * This allows the modification of extensions, integrations, etc.
730 */
731 public function pre_wc_init() {
732 $this->load_textdomain();
733 $this->load_dependencies();
734
735 $tos_accepted = WC_Connect_Options::get_option( 'tos_accepted' );
736
737 // Prevent presenting users with TOS they've already
738 // accepted in the core WC Setup Wizard or on WP.com.
739 if ( ! $tos_accepted &&
740 ( get_option( 'woocommerce_setup_jetpack_opted_in' ) || WC_Connect_Jetpack::is_atomic_site() )
741 ) {
742 WC_Connect_Options::update_option( 'tos_accepted', true );
743 delete_option( 'woocommerce_setup_jetpack_opted_in' );
744
745 $tos_accepted = true;
746 }
747
748 add_action( 'admin_init', array( $this, 'admin_enqueue_scripts' ) );
749 add_action( 'admin_init', array( $this->nux, 'set_up_nux_notices' ) );
750 add_filter( 'all_plugins', array( $this, 'maybe_rename_plugin' ) );
751
752 if ( WC_Connect_Nux::JETPACK_NOT_CONNECTED === $this->nux->get_jetpack_install_status() ) {
753 return;
754 }
755
756 add_action( 'rest_api_init', array( $this, 'tos_rest_init' ) );
757
758 // The entire plugin should be enabled if dev mode or connected + TOS.
759 if ( ! $tos_accepted ) {
760 return;
761 }
762
763 add_action( 'woocommerce_init', array( $this, 'after_wc_init' ) );
764 }
765
766 public function get_service_schema_defaults( $schema ) {
767 $defaults = array();
768
769 if ( ! property_exists( $schema, 'properties' ) ) {
770 return $defaults;
771 }
772
773 foreach ( get_object_vars( $schema->properties ) as $prop_id => $prop_schema ) {
774 if ( property_exists( $prop_schema, 'default' ) ) {
775 $defaults[ $prop_id ] = $prop_schema->default;
776 }
777
778 if (
779 property_exists( $prop_schema, 'type' ) &&
780 'object' === $prop_schema->type
781 ) {
782 $defaults[ $prop_id ] = $this->get_service_schema_defaults( $prop_schema );
783 }
784 }
785
786 return $defaults;
787 }
788
789 public function save_defaults_to_shipping_method( $instance_id, $service_id, $zone_id ) {
790 $shipping_method = WC_Shipping_Zones::get_shipping_method( $instance_id );
791 $schema = $shipping_method->get_service_schema();
792 $defaults = (object) $this->get_service_schema_defaults( $schema->service_settings );
793 WC_Connect_Options::update_shipping_method_option( 'form_settings', $defaults, $service_id, $instance_id );
794 }
795
796 protected function add_method_to_shipping_zone( $zone_id, $method_id ) {
797 $method = $this->get_service_schemas_store()->get_service_schema_by_id( $method_id );
798 if ( empty( $method ) ) {
799 return;
800 }
801
802 $zone = WC_Shipping_Zones::get_zone( $zone_id );
803 $instance_id = $zone->add_shipping_method( $method->method_id );
804 $zone->save();
805
806 // Dismiss the "add a method to zone" pointer.
807 $this->nux->dismiss_pointer( 'wc_services_add_service_to_zone' );
808 }
809
810 /**
811 * Bootstrap our plugin and hook into WP/WC core.
812 *
813 * @codeCoverageIgnore
814 */
815 public function after_wc_init() {
816 $this->schedule_service_schemas_fetch();
817 $this->service_settings_store->migrate_legacy_services();
818 $this->attach_hooks();
819 $this->init_store_notices();
820 $this->extend_store_api();
821 }
822
823 /**
824 * Load all plugin dependencies.
825 */
826 public function load_dependencies() {
827 require_once __DIR__ . '/classes/class-wc-connect-wcst-to-wcshipping-migration-state-enum.php';
828 require_once __DIR__ . '/classes/class-wc-connect-utils.php';
829 require_once __DIR__ . '/classes/class-wc-connect-logger.php';
830 require_once __DIR__ . '/classes/class-wc-connect-service-schemas-validator.php';
831 require_once __DIR__ . '/classes/class-wc-connect-taxjar-integration.php';
832 require_once __DIR__ . '/classes/class-wc-connect-custom-surcharge.php';
833 require_once __DIR__ . '/classes/class-wc-connect-error-notice.php';
834 require_once __DIR__ . '/classes/class-wc-connect-compatibility.php';
835 require_once __DIR__ . '/classes/class-wc-connect-compatibility-wcshipping-packages.php';
836 require_once __DIR__ . '/classes/class-wc-connect-shipping-method.php';
837 require_once __DIR__ . '/classes/class-wc-connect-service-schemas-store.php';
838 require_once __DIR__ . '/classes/class-wc-connect-service-settings-store.php';
839 require_once __DIR__ . '/classes/class-wc-connect-payment-methods-store.php';
840 require_once __DIR__ . '/classes/class-wc-connect-tracks.php';
841 require_once __DIR__ . '/classes/class-wc-connect-help-view.php';
842 require_once __DIR__ . '/classes/class-wc-connect-shipping-label.php';
843 require_once __DIR__ . '/classes/class-wc-connect-nux.php';
844 require_once __DIR__ . '/classes/class-wc-connect-paypal-ec.php';
845 require_once __DIR__ . '/classes/class-wc-connect-label-reports.php';
846 require_once __DIR__ . '/classes/class-wc-connect-privacy.php';
847 require_once __DIR__ . '/classes/class-wc-connect-account-settings.php';
848 require_once __DIR__ . '/classes/class-wc-connect-package-settings.php';
849 require_once __DIR__ . '/classes/class-wc-connect-continents.php';
850 require_once __DIR__ . '/classes/class-wc-connect-order-presenter.php';
851 require_once __DIR__ . '/classes/class-wc-connect-cart-validation.php';
852
853 $core_logger = new WC_Logger();
854 $logger = new WC_Connect_Logger( $core_logger );
855 $taxes_logger = new WC_Connect_Logger( $core_logger, 'taxes' );
856 $shipping_logger = new WC_Connect_Logger( $core_logger, 'shipping' );
857
858 WC_Connect_Compatibility_WCShipping_Packages::maybe_enable();
859
860 $validator = new WC_Connect_Service_Schemas_Validator();
861
862 if ( defined( 'WOOCOMMERCE_SERVICES_LOCAL_TEST_MODE' ) ) {
863 require_once __DIR__ . '/tests/php/class-wc-connect-api-client-local-test-mock.php';
864 $api_client = new WC_Connect_API_Client_Local_Test_Mock( $validator, $this );
865 } else {
866 require_once __DIR__ . '/classes/class-wc-connect-api-client-live.php';
867 $api_client = new WC_Connect_API_Client_Live( $validator, $this );
868 }
869 $schemas_store = new WC_Connect_Service_Schemas_Store( $api_client, $logger );
870 $settings_store = new WC_Connect_Service_Settings_Store( $schemas_store, $api_client, $logger );
871 $payment_methods_store = new WC_Connect_Payment_Methods_Store( $settings_store, $api_client, $logger );
872 $tracks = new WC_Connect_Tracks( $logger, __FILE__ );
873 $shipping_label = new WC_Connect_Shipping_Label( $api_client, $settings_store, $schemas_store, $payment_methods_store );
874 $nux = new WC_Connect_Nux( $tracks, $shipping_label );
875 $store_notices_notifier = new StoreNoticesNotifier( $taxes_logger->is_debug_enabled() );
876 $taxjar = new WC_Connect_TaxJar_Integration( $api_client, $taxes_logger, $this->wc_connect_base_url, $tracks, $store_notices_notifier );
877 $this->set_store_notices_notifier( $store_notices_notifier );
878 $paypal_ec = new WC_Connect_PayPal_EC( $api_client, $nux );
879 $label_reports = new WC_Connect_Label_Reports( $settings_store );
880
881 new WC_Connect_Privacy( $settings_store, $api_client );
882
883 $this->set_logger( $logger );
884 $this->set_shipping_logger( $shipping_logger );
885 $this->set_api_client( $api_client );
886 $this->set_service_schemas_validator( $validator );
887 $this->set_service_schemas_store( $schemas_store );
888 $this->set_service_settings_store( $settings_store );
889 $this->set_payment_methods_store( $payment_methods_store );
890 $this->set_tracks( $tracks );
891 $this->set_shipping_label( $shipping_label );
892 $this->set_nux( $nux );
893 $this->set_taxjar( $taxjar );
894 $this->set_paypal_ec( $paypal_ec );
895 $this->set_label_reports( $label_reports );
896
897 $cart_validation = new WC_Connect_Cart_Validation();
898 $cart_validation->register_filters();
899 $cart_validation->register_actions();
900 }
901
902 /**
903 * Load admin-only plugin dependencies.
904 */
905 public function load_admin_dependencies() {
906 require_once __DIR__ . '/classes/class-wc-connect-debug-tools.php';
907 new WC_Connect_Debug_Tools( $this->api_client );
908
909 $schema = $this->get_service_schemas_store();
910 $settings = $this->get_service_settings_store();
911 $logger = $this->get_logger();
912 $this->set_help_view( new WC_Connect_Help_View( $schema, $this->taxjar, $settings, $logger ) );
913 add_action( 'admin_notices', array( WC_Connect_Error_Notice::instance(), 'render_notice' ) );
914 add_action( 'admin_notices', array( $this, 'render_schema_notices' ) );
915
916 // Don't register settings if only_tax mode.
917 if ( ! self::should_load_shipping_features() ) {
918 return;
919 }
920
921 // We only use the settings page for shipping since tax settings are part of
922 // the core "WooCommerce > Settings > Tax" tab.
923 require_once __DIR__ . '/classes/class-wc-connect-settings-pages.php';
924 $settings_pages = new WC_Connect_Settings_Pages( $this->api_client, $this->get_service_schemas_store() );
925 $this->set_settings_pages( $settings_pages );
926
927 // Add WC Admin Notices.
928 if ( ! self::is_wc_shipping_activated() && self::can_add_wc_admin_notice() ) {
929 require_once __DIR__ . '/classes/class-wc-connect-note-dhl-live-rates-available.php';
930 WC_Connect_Note_DHL_Live_Rates_Available::init( $schema );
931 }
932 }
933
934 /**
935 * Hook plugin classes into WP/WC core.
936 */
937 public function attach_hooks() {
938 add_action( 'rest_api_init', array( $this, 'rest_api_init' ) );
939
940 add_action( 'admin_enqueue_scripts', array( $this->nux, 'show_pointers' ) );
941 add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'add_plugin_action_links' ) );
942
943 add_action( 'enqueue_wc_connect_script', array( $this, 'enqueue_wc_connect_script' ), 10, 2 );
944
945 add_action( 'wc_connect_fetch_sift_config', array( $this, 'background_fetch_sift_config' ) );
946
947 $tracks = $this->get_tracks();
948 $tracks->init();
949
950 $this->taxjar->init();
951 $this->paypal_ec->init();
952
953 // Only register shipping label-related logic if WC Shipping is not active.
954 if ( self::should_load_shipping_features() ) {
955 add_action( 'rest_api_init', array( $this, 'wc_api_dev_init' ), 9999 );
956
957 $this->init_shipping_labels();
958 }
959
960 /*
961 * Regardless of disabling shipping labels if WC Shipping is active,
962 * keep live rates enabled if the store supports them.
963 */
964 $this->init_live_rates();
965
966 if ( is_admin() ) {
967 $this->load_admin_dependencies();
968 }
969 }
970
971 /**
972 * Init WC Store Notices.
973 */
974 public function init_store_notices() {
975 new StoreNoticesController( $this->get_store_notices_notifier() );
976 }
977
978 /**
979 * Extend the Store API.
980 */
981 public function extend_store_api() {
982 $store_api_extend_schema = StoreApiExtendSchema::instance();
983 $store_api_extension_controller = new StoreApiExtensionController( $store_api_extend_schema );
984
985 // Register Store API extensions.
986 $store_api_extension_controller->register_extension( new StoreNoticesExtension( $store_api_extend_schema ) );
987
988 // Extend the Store API.
989 $store_api_extension_controller->extend_store();
990 }
991
992 /**
993 * Register shipping labels-related hooks.
994 *
995 * @return void
996 */
997 public function init_shipping_labels() {
998 add_filter( 'woocommerce_admin_reports', array( $this, 'reports_tabs' ) );
999
1000 // Initialize migration survey
1001 require_once __DIR__ . '/classes/class-wc-connect-migration-survey.php';
1002 new WC_Connect_Migration_Survey();
1003
1004 // Changing the postcode, currency, weight or dimension units affect the returned schema from the server.
1005 // Make sure to update the service schemas when these options change.
1006 // TODO: Add other options that change the schema here, or figure out a way to do it automatically.
1007 add_action( 'update_option_woocommerce_store_postcode', array( $this, 'queue_service_schema_refresh' ) );
1008 add_action( 'update_option_woocommerce_currency', array( $this, 'queue_service_schema_refresh' ) );
1009 add_action( 'update_option_woocommerce_weight_unit', array( $this, 'queue_service_schema_refresh' ) );
1010 add_action( 'update_option_woocommerce_dimension_unit', array( $this, 'queue_service_schema_refresh' ) );
1011 add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ), 5, 2 );
1012 add_action( 'woocommerce_admin_shipping_fields', array( $this, 'add_shipping_phone_to_order_fields' ) );
1013 add_filter( 'woocommerce_shipping_fields', array( $this, 'add_shipping_phone_to_checkout' ) );
1014 add_filter( 'woocommerce_get_order_address', array( $this, 'get_shipping_or_billing_phone_from_order' ), 10, 3 );
1015 add_action( 'woocommerce_email_after_order_table', array( $this, 'add_tracking_info_to_emails' ), 10, 3 );
1016 add_action( 'admin_print_footer_scripts', array( $this, 'add_sift_js_tracker' ) );
1017 add_filter( 'woocommerce_hidden_order_itemmeta', array( $this, 'hide_wc_connect_package_meta_data' ) );
1018 add_filter( 'is_protected_meta', array( $this, 'hide_wc_connect_order_meta_data' ), 10, 3 );
1019 add_action( 'current_screen', array( $this, 'maybe_render_upgrade_banner' ) );
1020 }
1021
1022 /**
1023 * Register live rates-related hooks.
1024 *
1025 * @return void
1026 */
1027 public function init_live_rates() {
1028 $schemas_store = $this->get_service_schemas_store();
1029 $schemas = $schemas_store->get_service_schemas();
1030
1031 if ( $schemas ) {
1032 add_filter( 'woocommerce_shipping_methods', array( $this, 'woocommerce_shipping_methods' ) );
1033 add_action( 'woocommerce_load_shipping_methods', array( $this, 'woocommerce_load_shipping_methods' ) );
1034 add_filter( 'woocommerce_payment_gateways', array( $this, 'woocommerce_payment_gateways' ) );
1035 add_action( 'wc_connect_service_init', array( $this, 'init_service' ), 10, 2 );
1036 add_action( 'wc_connect_service_admin_options', array( $this, 'localize_and_enqueue_service_script' ), 10, 2 );
1037 add_action( 'woocommerce_shipping_zone_method_added', array( $this, 'shipping_zone_method_added' ), 10, 3 );
1038 add_action( 'wc_connect_shipping_zone_method_added', array( $this, 'save_defaults_to_shipping_method' ), 10, 3 );
1039 add_action( 'woocommerce_shipping_zone_method_deleted', array( $this, 'shipping_zone_method_deleted' ), 10, 3 );
1040 add_action( 'woocommerce_shipping_zone_method_status_toggled', array( $this, 'shipping_zone_method_status_toggled' ), 10, 4 );
1041 }
1042
1043 add_action( 'wc_connect_fetch_service_schemas', array( $schemas_store, 'fetch_service_schemas_from_connect_server' ) );
1044 add_filter( 'wc_connect_shipping_service_settings', array( $this, 'shipping_service_settings' ), 10, 3 );
1045 add_action( 'woocommerce_checkout_order_processed', array( $this, 'track_completed_order' ), 10, 3 );
1046 }
1047
1048 /**
1049 * Queue up a service schema refresh (on shutdown) if there isn't one already.
1050 */
1051 public function queue_service_schema_refresh() {
1052 $schemas_store = $this->get_service_schemas_store();
1053
1054 if ( has_action( 'shutdown', array( $schemas_store, 'fetch_service_schemas_from_connect_server' ) ) ) {
1055 return;
1056 }
1057
1058 add_action( 'shutdown', array( $schemas_store, 'fetch_service_schemas_from_connect_server' ) );
1059 }
1060
1061 public function tos_rest_init() {
1062 $settings_store = $this->get_service_settings_store();
1063 $logger = $this->get_logger();
1064
1065 require_once __DIR__ . '/classes/class-wc-rest-connect-base-controller.php';
1066
1067 require_once __DIR__ . '/classes/class-wc-rest-connect-tos-controller.php';
1068 $rest_tos_controller = new WC_REST_Connect_Tos_Controller( $this->api_client, $settings_store, $logger );
1069 $this->set_rest_tos_controller( $rest_tos_controller );
1070 $rest_tos_controller->register_routes();
1071 }
1072
1073 /**
1074 * Hook the REST API
1075 * Note that we cannot load our controller until this time, because prior to
1076 * rest_api_init firing, WP_REST_Controller is not yet defined
1077 */
1078 public function rest_api_init() {
1079 $schemas_store = $this->get_service_schemas_store();
1080 $settings_store = $this->get_service_settings_store();
1081 $payment_methods_store = $this->get_payment_methods_store();
1082 $logger = $this->get_logger();
1083
1084 if ( ! class_exists( 'WP_REST_Controller' ) ) {
1085 $this->logger->debug( 'Error. WP_REST_Controller could not be found', __FUNCTION__ );
1086
1087 return;
1088 }
1089
1090 require_once __DIR__ . '/classes/class-wc-rest-connect-base-controller.php';
1091
1092 require_once __DIR__ . '/classes/class-wc-rest-connect-account-settings-controller.php';
1093 $rest_account_settings_controller = new WC_REST_Connect_Account_Settings_Controller( $this->api_client, $settings_store, $logger, $this->payment_methods_store );
1094 $this->set_rest_account_settings_controller( $rest_account_settings_controller );
1095 $rest_account_settings_controller->register_routes();
1096
1097 require_once __DIR__ . '/classes/class-wc-rest-connect-services-controller.php';
1098 $rest_services_controller = new WC_REST_Connect_Services_Controller( $this->api_client, $settings_store, $logger, $schemas_store );
1099 $this->set_rest_services_controller( $rest_services_controller );
1100 $rest_services_controller->register_routes();
1101
1102 require_once __DIR__ . '/classes/class-wc-rest-connect-self-help-controller.php';
1103 $rest_self_help_controller = new WC_REST_Connect_Self_Help_Controller( $this->api_client, $settings_store, $logger );
1104 $this->set_rest_self_help_controller( $rest_self_help_controller );
1105 $rest_self_help_controller->register_routes();
1106
1107 require_once __DIR__ . '/classes/class-wc-rest-connect-service-data-refresh-controller.php';
1108 $rest_service_data_refresh_controller = new WC_REST_Connect_Service_Data_Refresh_Controller( $this->api_client, $settings_store, $logger );
1109 $rest_service_data_refresh_controller->set_service_schemas_store( $this->get_service_schemas_store() );
1110 $rest_service_data_refresh_controller->register_routes();
1111
1112 if ( ! self::is_wc_shipping_activated() ) {
1113
1114 require_once __DIR__ . '/classes/class-wc-rest-connect-packages-controller.php';
1115 $rest_packages_controller = new WC_REST_Connect_Packages_Controller( $this->api_client, $settings_store, $logger, $this->service_schemas_store );
1116 $this->set_rest_packages_controller( $rest_packages_controller );
1117 $rest_packages_controller->register_routes();
1118
1119 require_once __DIR__ . '/classes/class-wc-rest-connect-shipping-label-controller.php';
1120 $rest_shipping_label_controller = new WC_REST_Connect_Shipping_Label_Controller( $this->api_client, $settings_store, $logger, $this->shipping_label, $this->payment_methods_store );
1121 $this->set_rest_shipping_label_controller( $rest_shipping_label_controller );
1122 $rest_shipping_label_controller->register_routes();
1123
1124 require_once __DIR__ . '/classes/class-wc-rest-connect-shipping-label-status-controller.php';
1125 $rest_shipping_label_status_controller = new WC_REST_Connect_Shipping_Label_Status_Controller( $this->api_client, $settings_store, $logger );
1126 $this->set_rest_shipping_label_status_controller( $rest_shipping_label_status_controller );
1127 $rest_shipping_label_status_controller->register_routes();
1128
1129 require_once __DIR__ . '/classes/class-wc-rest-connect-shipping-label-refund-controller.php';
1130 $rest_shipping_label_refund_controller = new WC_REST_Connect_Shipping_Label_Refund_Controller( $this->api_client, $settings_store, $logger );
1131 $this->set_rest_shipping_label_refund_controller( $rest_shipping_label_refund_controller );
1132 $rest_shipping_label_refund_controller->register_routes();
1133
1134 require_once __DIR__ . '/classes/class-wc-rest-connect-shipping-label-preview-controller.php';
1135 $rest_shipping_label_preview_controller = new WC_REST_Connect_Shipping_Label_Preview_Controller( $this->api_client, $settings_store, $logger );
1136 $this->set_rest_shipping_label_preview_controller( $rest_shipping_label_preview_controller );
1137 $rest_shipping_label_preview_controller->register_routes();
1138
1139 require_once __DIR__ . '/classes/class-wc-rest-connect-shipping-label-print-controller.php';
1140 $rest_shipping_label_print_controller = new WC_REST_Connect_Shipping_Label_Print_Controller( $this->api_client, $settings_store, $logger );
1141 $this->set_rest_shipping_label_print_controller( $rest_shipping_label_print_controller );
1142 $rest_shipping_label_print_controller->register_routes();
1143
1144 require_once __DIR__ . '/classes/class-wc-rest-connect-shipping-rates-controller.php';
1145 $rest_shipping_rates_controller = new WC_REST_Connect_Shipping_Rates_Controller( $this->api_client, $settings_store, $logger );
1146 $this->set_rest_shipping_rates_controller( $rest_shipping_rates_controller );
1147 $rest_shipping_rates_controller->register_routes();
1148
1149 require_once __DIR__ . '/classes/class-wc-rest-connect-address-normalization-controller.php';
1150 $rest_address_normalization_controller = new WC_REST_Connect_Address_Normalization_Controller( $this->api_client, $settings_store, $logger );
1151 $this->set_rest_address_normalization_controller( $rest_address_normalization_controller );
1152 $rest_address_normalization_controller->register_routes();
1153
1154 require_once __DIR__ . '/classes/class-wc-rest-connect-assets-controller.php';
1155 $rest_assets_controller = new WC_REST_Connect_Assets_Controller( $this->api_client, $settings_store, $logger );
1156 $this->set_rest_assets_controller( $rest_assets_controller );
1157 $rest_assets_controller->register_routes();
1158
1159 require_once __DIR__ . '/classes/class-wc-rest-connect-shipping-carrier-controller.php';
1160 $rest_carrier_controller = new WC_REST_Connect_Shipping_Carrier_Controller( $this->api_client, $settings_store, $logger );
1161 $this->set_rest_carrier_controller( $rest_carrier_controller );
1162 $rest_carrier_controller->register_routes();
1163
1164 require_once __DIR__ . '/classes/class-wc-rest-connect-subscription-activate-controller.php';
1165 $rest_subscription_activate_controller = new WC_REST_Connect_Subscription_activate_Controller( $this->api_client, $settings_store, $logger );
1166 $this->set_rest_subscription_activate_controller( $rest_subscription_activate_controller );
1167 $rest_subscription_activate_controller->register_routes();
1168
1169 require_once __DIR__ . '/classes/class-wc-rest-connect-shipping-carrier-delete-controller.php';
1170 $rest_carrier_delete_controller = new WC_REST_Connect_Shipping_Carrier_Delete_Controller( $this->api_client, $settings_store, $logger );
1171 $this->set_rest_carrier_delete_controller( $rest_carrier_delete_controller );
1172 $rest_carrier_delete_controller->register_routes();
1173
1174 require_once __DIR__ . '/classes/class-wc-rest-connect-shipping-carrier-types-controller.php';
1175 $rest_carrier_types_controller = new WC_REST_Connect_Shipping_Carrier_Types_Controller( $this->api_client, $settings_store, $logger );
1176 $this->set_carrier_types_controller( $rest_carrier_types_controller );
1177 $rest_carrier_types_controller->register_routes();
1178 }
1179 require_once __DIR__ . '/classes/class-wc-rest-connect-migration-flag-controller.php';
1180 $rest_migration_flag_controller = new WC_REST_Connect_Migration_Flag_Controller( $this->api_client, $settings_store, $logger, $this->tracks );
1181 $this->set_rest_migration_flag_controller( $rest_migration_flag_controller );
1182 $rest_migration_flag_controller->register_routes();
1183
1184 require_once __DIR__ . '/classes/class-wc-rest-connect-shipping-carriers-controller.php';
1185 $rest_carriers_controller = new WC_REST_Connect_Shipping_Carriers_Controller( $this->api_client, $settings_store, $logger );
1186 $this->set_rest_carriers_controller( $rest_carriers_controller );
1187 $rest_carriers_controller->register_routes();
1188
1189 require_once __DIR__ . '/classes/class-wc-rest-connect-subscriptions-controller.php';
1190 $rest_subscriptions_controller = new WC_REST_Connect_Subscriptions_Controller( $this->api_client, $settings_store, $logger );
1191 $this->set_rest_subscriptions_controller( $rest_subscriptions_controller );
1192 $rest_subscriptions_controller->register_routes();
1193
1194 require_once __DIR__ . '/classes/class-wc-rest-connect-shipping-label-eligibility-controller.php';
1195 $rest_shipping_label_eligibility_controller = new WC_REST_Connect_Shipping_Label_Eligibility_Controller(
1196 $this->api_client,
1197 $settings_store,
1198 $logger,
1199 $this->shipping_label,
1200 $this->payment_methods_store,
1201 self::has_only_tax_functionality()
1202 );
1203
1204 $rest_shipping_label_eligibility_controller->register_routes();
1205
1206 /**
1207 * We need 4 objects instantiated in `WC_Connect_Loader` to construct WC_REST_Connect_WCShipping_Compatibility_Packages_Controller.
1208 *
1209 * Since these objects are created here but the call to
1210 * WC_Connect_Compatibility_WCShipping_Packages::maybe_enable() happens earlier,
1211 * to keep the REST controller instantiation within that class this hook is introduced,
1212 * passing the `WC_Connect_Loader` as an argument.
1213 *
1214 * @see WC_Connect_Compatibility_WCShipping_Packages::register_rest_controller_hooks
1215 */
1216 do_action( 'wcservices_rest_api_init', $this );
1217
1218 add_filter( 'rest_request_before_callbacks', array( $this, 'log_rest_api_errors' ), 10, 3 );
1219 }
1220
1221 /**
1222 * If the required v3 REST API endpoints haven't been loaded at this point, load the local copies of said endpoints.
1223 * Delete this when the "v3" REST API is included in all the WC versions we support.
1224 */
1225 public function wc_api_dev_init() {
1226 $rest_server = rest_get_server();
1227 $existing_routes = $rest_server->get_routes();
1228 if ( ! isset( $existing_routes['/wc/v3/data/continents'] ) ) {
1229 require_once __DIR__ . '/classes/wc-api-dev/class-wc-rest-dev-data-controller.php';
1230 require_once __DIR__ . '/classes/wc-api-dev/class-wc-rest-dev-data-continents-controller.php';
1231 $continents = new WC_REST_Dev_Data_Continents_Controller();
1232 $continents->register_routes();
1233 }
1234 }
1235
1236 /**
1237 * Log any WP_Errors encountered before our REST API callbacks
1238 *
1239 * Note: intended to be hooked into 'rest_request_before_callbacks'
1240 *
1241 * @param WP_HTTP_Response $response Result to send to the client. Usually a WP_REST_Response.
1242 * @param WP_REST_Server $handler ResponseHandler instance (usually WP_REST_Server).
1243 * @param WP_REST_Request $request Request used to generate the response.
1244 *
1245 * @return mixed - pass through value of $response.
1246 */
1247 public function log_rest_api_errors( $response, $handler, $request ) {
1248 if ( ! is_wp_error( $response ) ) {
1249 return $response;
1250 }
1251
1252 if ( 0 === strpos( $request->get_route(), '/wc/v1/connect/' ) ) {
1253 $route_info = $request->get_method() . ' ' . $request->get_route();
1254
1255 $this->get_logger()->error( $response, $route_info );
1256 $this->get_logger()->error( $route_info, $request->get_body() );
1257 }
1258
1259 return $response;
1260 }
1261
1262 /**
1263 * Added to the wc_connect_shipping_service_settings filter, returns service settings
1264 *
1265 * @param $settings
1266 * @param $method_id
1267 * @param $instance_id
1268 *
1269 * @return array
1270 */
1271 public function shipping_service_settings( $settings, $method_id, $instance_id ) {
1272 $settings_store = $this->get_service_settings_store();
1273 $schemas_store = $this->get_service_schemas_store();
1274 $service_schema = $schemas_store->get_service_schema_by_id_or_instance_id( $instance_id ? $instance_id : $method_id );
1275 if ( ! $service_schema ) {
1276 return array_merge(
1277 $settings,
1278 array(
1279 'formType' => 'services',
1280 'methodId' => $method_id,
1281 'instanceId' => $instance_id,
1282 )
1283 );
1284 }
1285
1286 return array_merge(
1287 $settings,
1288 array(
1289 'storeOptions' => $settings_store->get_store_options(),
1290 'formSchema' => $service_schema->service_settings,
1291 'formLayout' => $service_schema->form_layout,
1292 'formData' => $settings_store->get_service_settings( $method_id, $instance_id ),
1293 'formType' => 'services',
1294 'methodId' => $method_id,
1295 'instanceId' => $instance_id,
1296 )
1297 );
1298 }
1299
1300 /**
1301 * Load shipping method settings.
1302 *
1303 * This function is added to the wc_connect_service_admin_options action by this class
1304 * (see attach_hooks) and then that action is fired by WC_Connect_Shipping_Method::admin_options
1305 * to get the service instance form layout and settings bundled inside wcConnectData
1306 * as the form container is emitted into the body's HTML
1307 */
1308 public function localize_and_enqueue_service_script( $method_id, $instance_id = false ) {
1309 if ( ! function_exists( 'get_rest_url' ) ) {
1310 return;
1311 }
1312
1313 do_action(
1314 'enqueue_wc_connect_script',
1315 'wc-connect-service-settings',
1316 array(
1317 'methodId' => $method_id,
1318 'instanceId' => $instance_id,
1319 )
1320 );
1321 }
1322
1323 /**
1324 * Filter function for adding the report tabs
1325 *
1326 * @param array $reports - report tabs meta.
1327 * @return array report tabs with WCS tabs added.
1328 */
1329 public function reports_tabs( $reports ) {
1330 $reports['wcs_labels'] = array(
1331 'title' => __( 'Shipping Labels', 'woocommerce-services' ),
1332 'reports' => array(
1333 'connect_labels' => array(
1334 'title' => __( 'Shipping Labels', 'woocommerce-services' ),
1335 'description' => '',
1336 'hide_title' => true,
1337 'callback' => array( $this->label_reports, 'output_report' ),
1338 ),
1339 ),
1340 );
1341
1342 return $reports;
1343 }
1344
1345 /**
1346 * Send completed order details to Connect Server to check live rates usage.
1347 * Attached to the woocommerce_order_details_after_order_table hook
1348 *
1349 * @param int $order_id int for completed order ID.
1350 * @param array $data Post data for order.
1351 * @param array $order WC_Order object containing completed order details.
1352 */
1353 public function track_completed_order( $order_id, $data, $order ) {
1354 $logger = $this->get_logger();
1355 $services = array();
1356 $packages = WC()->cart->get_shipping_packages();
1357
1358 foreach ( $packages as $package ) {
1359 $shipping_methods = WC()->shipping()->load_shipping_methods( $package );
1360 $shipping_packages = WC()->shipping()->calculate_shipping_for_package( $package );
1361
1362 // Only process the valid shipping package.
1363 if ( empty( $shipping_packages['rates'] ) || ! is_array( $shipping_packages['rates'] ) ) {
1364 continue;
1365 }
1366
1367 $shipping_rates = $shipping_packages['rates'];
1368
1369 $shipping_method_ids = array();
1370 foreach ( $shipping_rates as $rate ) {
1371 if ( ! in_array( $rate->method_id, $shipping_method_ids, true ) ) {
1372 $shipping_method_ids[] = $rate->method_id;
1373 }
1374 }
1375
1376 foreach ( $shipping_methods as $method ) {
1377 if ( $method instanceof WC_Connect_Shipping_Method ) {
1378 $service_settings = $method->get_service_settings();
1379 $service_schema = $method->get_service_schema();
1380 $instance_id = $method->get_instance_id();
1381 $service_id = $service_schema->id;
1382
1383 if ( is_null( $instance_id ) || ! in_array( "wc_services_$service_id", $shipping_method_ids, true ) ) {
1384 continue;
1385 }
1386
1387 $services[] = array(
1388 'id' => $service_id,
1389 'instance' => $instance_id,
1390 'service_settings' => $service_settings,
1391 );
1392 }
1393 }
1394 }
1395
1396 if ( empty( $services ) ) {
1397 return;
1398 }
1399
1400 $api_client = $this->get_api_client();
1401 $response = $api_client->track_subscription_event( $services );
1402 if ( is_wp_error( $response ) ) {
1403 if ( is_a( $logger, 'WC_Connect_Logger' ) ) {
1404 $logger->error(
1405 sprintf(
1406 'Unable to send subscription event: %s',
1407 $response->get_error_message()
1408 ),
1409 __FUNCTION__
1410 );
1411 }
1412 }
1413 }
1414
1415 /**
1416 * Add tracking info (if available) to completed emails using the woocommerce_email_after_order_table hook
1417 *
1418 * @param bool|\WC_Order|\WC_Order_Refund $order
1419 * @param $sent_to_admin
1420 * @param $plain_text
1421 */
1422 public function add_tracking_info_to_emails( $order, $sent_to_admin, $plain_text ) {
1423
1424 // Abort if no $order was passed, if the order is not marked as 'completed' or if another extension is handling the emailing.
1425 if ( ! $order
1426 || ! $order->has_status( 'completed' )
1427 || ! WC_Connect_Extension_Compatibility::should_email_tracking_details( $order->get_id() ) ) {
1428 return;
1429 }
1430
1431 $labels = $this->service_settings_store->get_label_order_meta_data( $order->get_id() );
1432
1433 // Abort if there are no labels.
1434 if ( empty( $labels ) ) {
1435 return;
1436 }
1437
1438 $markup = '';
1439 $link_color = get_option( 'woocommerce_email_text_color' );
1440
1441 // Generate a table row for each label.
1442 foreach ( $labels as $label ) {
1443 $carrier = $label['carrier_id'];
1444 $carrier_service = $this->get_service_schemas_store()->get_service_schema_by_id( $carrier );
1445 $carrier_label = ( ! $carrier_service || empty( $carrier_service->carrier_name ) ) ? strtoupper( $carrier ) : $carrier_service->carrier_name;
1446 $tracking = $label['tracking'];
1447 $error = array_key_exists( 'error', $label );
1448 $refunded = array_key_exists( 'refund', $label );
1449
1450 // If the label has an error or is refunded, move to the next label.
1451 if ( $error || $refunded ) {
1452 continue;
1453 }
1454
1455 if ( $plain_text ) {
1456 // Should look like '- USPS: 9405536897846173912345' in plain text mode.
1457 $markup .= '- ' . $carrier_label . ': ' . $tracking . "\n";
1458 continue;
1459 }
1460
1461 $markup .= '<tr>';
1462 $markup .= '<td class="td" scope="col">' . esc_html( $carrier_label ) . '</td>';
1463
1464 switch ( $carrier ) {
1465 case 'fedex':
1466 $tracking_url = 'https://www.fedex.com/apps/fedextrack/?action=track&tracknumbers=' . $tracking;
1467 break;
1468 case 'usps':
1469 $tracking_url = 'https://tools.usps.com/go/TrackConfirmAction.action?tLabels=' . $tracking;
1470 break;
1471 case 'ups':
1472 $tracking_url = 'https://www.ups.com/track?tracknum=' . $tracking;
1473 break;
1474 case 'dhlexpress':
1475 $tracking_url = 'https://www.dhl.com/en/express/tracking.html?AWB=' . $tracking . '&brand=DHL';
1476 break;
1477 }
1478
1479 $markup .= '<td class="td" scope="col">';
1480 $markup .= '<a href="' . esc_url( $tracking_url ) . '" style="color: ' . esc_attr( $link_color ) . '">' . esc_html( $tracking ) . '</a>';
1481 $markup .= '</td>';
1482 $markup .= '</tr>';
1483 }
1484
1485 // Abort if all labels are refunded.
1486 if ( empty( $markup ) ) {
1487 return;
1488 }
1489
1490 if ( $plain_text ) {
1491 echo "\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n";
1492 echo esc_html( mb_strtoupper( __( 'Tracking', 'woocommerce-services' ), 'UTF-8' ) ) . "\n\n";
1493 echo wp_kses( $markup, array() );
1494 return;
1495 }
1496
1497 ?>
1498 <div style="font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif; margin-bottom: 40px;">
1499 <h2><?php esc_html_e( 'Tracking', 'woocommerce-services' ); ?></h2>
1500 <table class="td" cellspacing="0" cellpadding="6" style="margin-top: 10px; width: 100%;">
1501 <thead>
1502 <tr>
1503 <th class="td" scope="col"><?php esc_html_e( 'Provider', 'woocommerce-services' ); ?></th>
1504 <th class="td" scope="col"><?php esc_html_e( 'Tracking number', 'woocommerce-services' ); ?></th>
1505 </tr>
1506 </thead>
1507 <tbody>
1508 <?php echo wp_kses_post( $markup ); ?>
1509 </tbody>
1510 </table>
1511 </div>
1512 <?php
1513 }
1514
1515 /**
1516 * Hook fetching the available services from the connect server
1517 */
1518 public function schedule_service_schemas_fetch() {
1519 $schemas_store = $this->get_service_schemas_store();
1520 $schemas = $schemas_store->get_service_schemas();
1521 $last_fetch_result = $schemas_store->get_last_fetch_result_code();
1522
1523 if ( ! $schemas && '401' !== $last_fetch_result ) { // Don't retry auth failures wait for next scheduled time.
1524 $schemas_store->fetch_service_schemas_from_connect_server();
1525 } elseif ( defined( 'WOOCOMMERCE_CONNECT_FREQUENT_FETCH' ) && WOOCOMMERCE_CONNECT_FREQUENT_FETCH ) {
1526 $schemas_store->fetch_service_schemas_from_connect_server();
1527 } elseif ( ! wp_next_scheduled( 'wc_connect_fetch_service_schemas' ) ) {
1528 wp_schedule_event( time(), 'daily', 'wc_connect_fetch_service_schemas' );
1529 }
1530 }
1531
1532 /**
1533 * Inject API Client and Logger into WC Connect shipping method instances.
1534 *
1535 * @param WC_Connect_Shipping_Method $method
1536 * @param int|string $id_or_instance_id
1537 */
1538 public function init_service( WC_Connect_Shipping_Method $method, $id_or_instance_id ) {
1539
1540 // TODO - make more generic - allow things other than WC_Connect_Shipping_Method to work here
1541
1542 $method->set_api_client( $this->get_api_client() );
1543 $method->set_logger( $this->get_shipping_logger() );
1544 $method->set_service_settings_store( $this->get_service_settings_store() );
1545
1546 $service_schema = $this->get_service_schemas_store()->get_service_schema_by_id_or_instance_id( $id_or_instance_id );
1547
1548 if ( $service_schema ) {
1549 $method->set_service_schema( $service_schema );
1550 }
1551 }
1552
1553 /**
1554 * Returns a reference to a service (e.g. WC_Connect_Shipping_Method) of
1555 * a particular id so we can avoid instantiating them multiple times
1556 *
1557 * @param string $class_name Class name of service to create (e.g. WC_Connect_Shipping_Method)
1558 * @param string $service_id Service id of service to create (e.g. usps)
1559 * @return mixed
1560 */
1561 protected function get_service_object_by_id( $class_name, $service_id ) {
1562 if ( ! array_key_exists( $service_id, $this->service_object_cache ) ) {
1563 $this->service_object_cache[ $service_id ] = new $class_name( $service_id );
1564 }
1565
1566 return $this->service_object_cache[ $service_id ];
1567 }
1568
1569 /**
1570 * Filters in shipping methods for things like WC_Shipping::get_shipping_method_class_names
1571 *
1572 * @param $shipping_methods
1573 * @return mixed
1574 */
1575 public function woocommerce_shipping_methods( $shipping_methods ) {
1576 $shipping_service_ids = $this->get_service_schemas_store()->get_all_shipping_method_ids();
1577
1578 foreach ( $shipping_service_ids as $shipping_service_id ) {
1579 $shipping_methods[ $shipping_service_id ] = $this->get_service_object_by_id( 'WC_Connect_Shipping_Method', $shipping_service_id );
1580 }
1581
1582 return $shipping_methods;
1583 }
1584
1585 /**
1586 * Registers shipping methods for use in things like the Add Shipping Method dialog
1587 * on the Shipping Zones view
1588 */
1589 public function woocommerce_load_shipping_methods() {
1590 $shipping_service_ids = $this->get_service_schemas_store()->get_all_shipping_method_ids();
1591
1592 foreach ( $shipping_service_ids as $shipping_service_id ) {
1593 $shipping_method = $this->get_service_object_by_id( 'WC_Connect_Shipping_Method', $shipping_service_id );
1594 WC_Shipping::instance()->register_shipping_method( $shipping_method );
1595 }
1596 }
1597
1598 public function woocommerce_payment_gateways( $payment_gateways ) {
1599 return $payment_gateways;
1600 }
1601
1602 private function get_i18n_json() {
1603 $i18n_json = plugin_dir_path( __FILE__ ) . 'i18n/languages/woocommerce-services-' . get_locale() . '.json';
1604 if ( is_file( $i18n_json ) && is_readable( $i18n_json ) ) {
1605 $locale_data = @file_get_contents( $i18n_json );
1606 if ( $locale_data ) {
1607 return $locale_data;
1608 }
1609 }
1610 // Return empty if we have nothing to return so it doesn't fail when parsed in JS.
1611 return '{}';
1612 }
1613
1614 /**
1615 * Return true if we are on the order list page wp-admin/edit.php?post_type=shop_order.
1616 *
1617 * @return boolean
1618 */
1619 public function should_render_upgrade_banner() {
1620 if ( ! is_admin() ) {
1621 return false;
1622 }
1623
1624 $screen = get_current_screen();
1625 if ( ! $screen || ! isset( $screen->id ) ) {
1626 return false;
1627 }
1628
1629 if ( ! function_exists( 'wc_get_page_screen_id' ) ) {
1630 return false;
1631 }
1632
1633 $wc_order_screen_id = wc_get_page_screen_id( 'shop_order' );
1634 if ( ! $wc_order_screen_id ) {
1635 return false;
1636 }
1637
1638 // Order list page doesn't have the action parameter in the querystring.
1639 if ( isset( $_GET['action'] ) ) {
1640 return false;
1641 }
1642
1643 // All WC settings pages
1644 if ( $screen->id === 'woocommerce_page_wc-settings' ) {
1645 return true;
1646 }
1647
1648 /*
1649 * Non-HPOS:
1650 * $screen->id = "edit-shop_order"
1651 * $wc_order_screen_id = "shop_order"
1652 *
1653 * HPOS:
1654 * $screen->id = "woocommerce_page_wc-orders"
1655 * $$wc_order_screen_id = "woocommerce_page_wc-orders"
1656 */
1657 if ( $screen->id !== 'edit-shop_order' && $screen->id !== 'woocommerce_page_wc-orders' ) {
1658 return false;
1659 }
1660
1661 return true;
1662 }
1663
1664 public function maybe_render_upgrade_banner() {
1665 if ( ! $this->should_render_upgrade_banner() ) {
1666 // If this is not on the order list page, then don't add any action.
1667 return;
1668 }
1669
1670 // Add the WCS&T to WCShipping migration notice, creating a button to update.
1671 $settings_store = $this->get_service_settings_store();
1672 if ( $settings_store->is_eligible_for_migration() && $this->shipping_label->is_store_eligible_for_shipping_label_creation() ) {
1673 add_action( 'admin_notices', array( $this, 'display_wcst_to_wcshipping_migration_notice' ) );
1674 add_action( 'admin_notices', array( $this, 'register_wcshipping_migration_modal' ) );
1675 }
1676 }
1677
1678 /**
1679 * Registers the React UI bundle
1680 */
1681 public function admin_enqueue_scripts() {
1682 $plugin_version = self::get_wcs_version();
1683
1684 wp_register_script( 'wc_connect_admin', self::get_wcs_admin_script_url(), array( 'lodash', 'moment', 'react', 'react-dom' ), null, true ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
1685 // Dev mode will handle loading the css itself to support HMR.
1686 $stylesheet_url = self::get_wcs_admin_style_url();
1687 if ( ! empty( $stylesheet_url ) ) {
1688 // Load CSS async to prevent blocking rendering since this css is non-essential.
1689 wp_add_inline_script(
1690 'wc_connect_admin',
1691 "var link = document.createElement('link');link.rel = 'stylesheet';link.type = 'text/css';link.href = '" . esc_js( $stylesheet_url ) . "';document.getElementsByTagName('HEAD')[0].appendChild(link);"
1692 );
1693 }
1694
1695 wp_register_script( 'wc_services_admin_pointers', $this->wc_connect_base_url . 'woocommerce-services-admin-pointers-' . $plugin_version . '.js', array( 'wp-pointer', 'jquery' ), null );
1696 wp_register_style( 'wc_connect_banner', $this->wc_connect_base_url . 'woocommerce-services-banner-' . $plugin_version . '.css', array(), null );
1697 wp_register_script( 'wc_connect_banner', $this->wc_connect_base_url . 'woocommerce-services-banner-' . $plugin_version . '.js', array(), null );
1698
1699 $i18n_json = $this->get_i18n_json();
1700 // JS translations loaded from i18n/languages/woocommerce-services-{LOCALE}.json via get_i18n_json().
1701 wp_localize_script(
1702 'wc_connect_admin',
1703 'i18nLocale',
1704 array(
1705 'json' => $i18n_json,
1706 'localeSlug' => join( '-', explode( '_', get_locale() ) ),
1707 )
1708 );
1709 wp_localize_script(
1710 'wc_connect_admin',
1711 'wcsPluginData',
1712 array(
1713 'assetPath' => self::get_wc_connect_base_url(),
1714 'adminPluginPath' => admin_url( 'plugins.php' ),
1715 )
1716 );
1717 }
1718
1719 public function get_active_shipping_services() {
1720 global $wpdb;
1721 $active_shipping_services = array();
1722 $shipping_service_ids = $this->get_service_schemas_store()->get_all_shipping_method_ids();
1723
1724 foreach ( $shipping_service_ids as $shipping_service_id ) {
1725 $is_active = $wpdb->get_var(
1726 $wpdb->prepare(
1727 "SELECT instance_id FROM {$wpdb->prefix}woocommerce_shipping_zone_methods WHERE is_enabled = 1 AND method_id = %s LIMIT 1;",
1728 $shipping_service_id
1729 )
1730 );
1731
1732 if ( $is_active ) {
1733 $active_shipping_services[] = $shipping_service_id;
1734 }
1735 }
1736
1737 return $active_shipping_services;
1738 }
1739
1740 public function get_active_services() {
1741 return $this->get_active_shipping_services();
1742 }
1743
1744 public function is_wc_connect_shipping_service( $service_id ) {
1745 $shipping_service_ids = $this->get_service_schemas_store()->get_all_shipping_method_ids();
1746 return in_array( $service_id, $shipping_service_ids );
1747 }
1748
1749 public function shipping_zone_method_added( $instance_id, $service_id, $zone_id ) {
1750 if ( $this->is_wc_connect_shipping_service( $service_id ) ) {
1751 do_action( 'wc_connect_shipping_zone_method_added', $instance_id, $service_id, $zone_id );
1752 }
1753 }
1754
1755 public function shipping_zone_method_deleted( $instance_id, $service_id, $zone_id ) {
1756 if ( $this->is_wc_connect_shipping_service( $service_id ) ) {
1757 WC_Connect_Options::delete_shipping_method_options( $service_id, $instance_id );
1758 do_action( 'wc_connect_shipping_zone_method_deleted', $instance_id, $service_id, $zone_id );
1759 }
1760 }
1761
1762 public function shipping_zone_method_status_toggled( $instance_id, $service_id, $zone_id, $enabled ) {
1763 if ( $this->is_wc_connect_shipping_service( $service_id ) ) {
1764 do_action( 'wc_connect_shipping_zone_method_status_toggled', $instance_id, $service_id, $zone_id, $enabled );
1765 }
1766 }
1767
1768 public function should_show_shipping_debug_meta_box( $post ) {
1769 $order = WC_Connect_Compatibility::instance()->init_theorder_object( $post );
1770
1771 if ( false === $order ) {
1772 return false;
1773 }
1774
1775 $shipping_methods = $order->get_shipping_methods();
1776
1777 foreach ( $shipping_methods as $method ) {
1778 if ( ! empty( $method['wc_connect_packing_log'] ) ) {
1779 return true;
1780 }
1781 }
1782
1783 return false;
1784 }
1785
1786 public function add_meta_boxes( $screen_id, $post ) {
1787
1788 if ( $this->shipping_label->should_show_meta_box( $post ) ) {
1789 add_meta_box( 'woocommerce-order-shipment-tracking', __( 'Shipment Tracking', 'woocommerce-services' ), array( $this->shipping_label, 'meta_box' ), null, 'side', 'default', array( 'context' => 'shipment_tracking' ) );
1790
1791 add_meta_box( 'woocommerce-order-label', __( 'Shipping Label', 'woocommerce-services' ), array( $this->shipping_label, 'meta_box' ), null, 'normal', 'high', array( 'context' => 'shipping_label' ) );
1792 }
1793
1794 if ( $this->should_show_shipping_debug_meta_box( $post ) ) {
1795 $screen = WC_Connect_Compatibility::instance()->get_order_admin_screen();
1796 add_meta_box( 'woocommerce-services-shipping-debug', __( 'Shipping Debug', 'woocommerce-services' ), array( $this, 'shipping_rate_packaging_debug_log_meta_box' ), $screen, 'normal', 'default' );
1797 }
1798 }
1799
1800 public function shipping_rate_packaging_debug_log_meta_box( $post ) {
1801 $order = wc_get_order( $post );
1802 $shipping_methods = $order->get_shipping_methods();
1803
1804 foreach ( $shipping_methods as $method ) {
1805 if ( empty( $method['wc_connect_packing_log'] ) ) {
1806 continue;
1807 }
1808
1809 $method_instance = new WC_Connect_Shipping_Method( $method->get_instance_id() );
1810 ?>
1811 <p>
1812 <strong><?php esc_html_e( 'Shipping Method Name:', 'woocommerce-services' ); ?></strong>
1813 <?php echo esc_html( $method_instance->get_method_title() ); ?>
1814 </p>
1815 <p>
1816 <strong><?php esc_html_e( 'Shipping Method ID:', 'woocommerce-services' ); ?> </strong>
1817 <?php echo esc_html( $method->get_method_id() . ':' . $method->get_instance_id() ); ?>
1818 </p>
1819 <p>
1820 <strong><?php esc_html_e( 'Chosen Rate:', 'woocommerce-services' ); ?> </strong>
1821 <?php
1822 echo esc_html(
1823 printf(
1824 '%s (%s%s)',
1825 $method->get_name(),
1826 get_woocommerce_currency_symbol(),
1827 $method->get_total()
1828 )
1829 );
1830 ?>
1831 </p>
1832 <p>
1833 <strong><?php esc_html_e( 'Packing Log:', 'woocommerce-services' ); ?> </strong>
1834 </p>
1835 <pre class="packing-log"><?php echo esc_html( implode( "\n", $method['wc_connect_packing_log'] ) ); ?></pre>
1836 <?php
1837 }
1838 }
1839
1840 public function hide_wc_connect_package_meta_data( $hidden_keys ) {
1841 $hidden_keys[] = 'wc_connect_packages';
1842 $hidden_keys[] = 'wc_connect_packing_log';
1843 return $hidden_keys;
1844 }
1845
1846 public function hide_wc_connect_order_meta_data( $protected, $meta_key, $meta_type ) {
1847 if ( in_array( $meta_key, array( 'wc_connect_labels', 'wc_connect_destination_normalized' ), true ) ) {
1848 $protected = true;
1849 }
1850
1851 return $protected;
1852 }
1853
1854 public function add_shipping_phone_to_checkout( $fields ) {
1855 $defaults = array(
1856 'label' => __( 'Phone', 'woocommerce-services' ),
1857 'type' => 'tel',
1858 'required' => false,
1859 'class' => array( 'form-row-wide' ),
1860 'clear' => true,
1861 'validate' => array( 'phone' ),
1862 'autocomplete' => 'tel',
1863 );
1864
1865 // Use existing settings if the field exists.
1866 $field = isset( $fields['shipping_phone'] )
1867 ? array_merge( $defaults, $fields['shipping_phone'] )
1868 : $defaults;
1869
1870 // Enforce phone type, autocomplete, and validation.
1871 $field['type'] = 'tel';
1872 $field['autocomplete'] = 'tel';
1873 if ( ! in_array( 'tel', $field['validate'], true ) ) {
1874 $field['validate'][] = 'tel';
1875 }
1876
1877 // Add to the list.
1878 $fields['shipping_phone'] = $field;
1879 return $fields;
1880 }
1881
1882 public function add_shipping_phone_to_order_fields( $fields ) {
1883 $fields['phone'] = array(
1884 'label' => __( 'Phone', 'woocommerce-services' ),
1885 );
1886 return $fields;
1887 }
1888
1889 public function get_shipping_or_billing_phone_from_order( $fields, $address_type, WC_Order $order ) {
1890 if ( 'shipping' !== $address_type ) {
1891 return $fields;
1892 }
1893
1894 $fields['phone'] = $order->get_shipping_phone() ? $order->get_shipping_phone() : $order->get_billing_phone();
1895
1896 return $fields;
1897 }
1898
1899 public function add_plugin_action_links( $links ) {
1900 $links[] = sprintf(
1901 wp_kses(
1902 /* translators: %s Support url */
1903 __( '<a href="%s">Support</a>', 'woocommerce-services' ),
1904 array( 'a' => array( 'href' => array() ) )
1905 ),
1906 esc_url( 'https://woocommerce.com/my-account/create-a-ticket/' )
1907 );
1908 return $links;
1909 }
1910
1911 /**
1912 * Performs a direct database check to see if any order has shipping labels.
1913 * Static method to be callable from early hooks like 'all_plugins'.
1914 *
1915 * @return bool True if any labels exist, false otherwise.
1916 */
1917 private static function _has_any_labels_db_check() {
1918 global $wpdb;
1919
1920 // If $wpdb is not available yet, assume no labels (safer default).
1921 if ( ! is_object( $wpdb ) ) {
1922 return false;
1923 }
1924
1925 $meta_table_to_check = OrderUtil::get_table_for_order_meta();
1926
1927 return (bool) $wpdb->get_var(
1928 $wpdb->prepare(
1929 'SELECT EXISTS (
1930 SELECT 1
1931 FROM %i
1932 WHERE meta_key = %s
1933 LIMIT 1
1934 )',
1935 $meta_table_to_check,
1936 'wc_connect_labels'
1937 )
1938 );
1939 }
1940
1941 /**
1942 * Determines if the store is configured for tax-only functionality.
1943 *
1944 * This method checks two conditions:
1945 * 1. If Jetpack is connected and the 'only_tax' option is set to '1', it returns true.
1946 * 2. If Jetpack is not connected and there are no legacy shipping labels in the database, it returns true.
1947 *
1948 * @return bool True if the store is configured for tax-only functionality, false otherwise.
1949 */
1950 public static function has_only_tax_functionality() {
1951 $result = ( WC_Connect_Jetpack::is_connected() && '1' === WC_Connect_Options::get_option( 'only_tax' ) ) ||
1952 ( ! WC_Connect_Jetpack::is_connected() && ! self::_has_any_labels_db_check() );
1953
1954 // Allow tests to override this functionality.
1955 return apply_filters( 'wc_connect_has_only_tax_functionality', $result );
1956 }
1957
1958 /**
1959 * Checks whether shipping-related functionality and views should be loaded.
1960 *
1961 * Shipping features should only be loaded when the WooCommerce Shipping plugin
1962 * is not active and the site is not restricted to tax-only functionality.
1963 *
1964 * @return bool True if shipping features should be loaded, false otherwise.
1965 */
1966 public static function should_load_shipping_features(): bool {
1967 return ! self::is_wc_shipping_activated() && ! self::has_only_tax_functionality();
1968 }
1969
1970 public function maybe_rename_plugin( $plugins ) {
1971 $plugin_basename = 'woocommerce-services/woocommerce-services.php';
1972
1973 if ( isset( $plugins[ $plugin_basename ] ) ) {
1974 if ( self::has_only_tax_functionality() ) {
1975 $plugins[ $plugin_basename ]['Name'] = $this->get_plugin_name_for_new_sites();
1976 $plugins[ $plugin_basename ]['Description'] = $this->get_plugin_description_for_new_sites();
1977 } else {
1978 $plugins[ $plugin_basename ]['Name'] = $this->get_plugin_name_for_legacy_sites();
1979 $plugins[ $plugin_basename ]['Description'] = $this->get_plugin_description_for_legacy_sites();
1980 }
1981 }
1982 return $plugins;
1983 }
1984
1985 private function get_plugin_name_for_legacy_sites() {
1986 return __( 'WooCommerce Tax (previously WooCommerce Shipping & Tax)', 'woocommerce-services' );
1987 }
1988
1989 private function get_plugin_description_for_legacy_sites() {
1990 return __( 'Hosted services for WooCommerce: automated tax calculation, shipping label printing, and smoother payment setup.', 'woocommerce-services' );
1991 }
1992
1993 private function get_plugin_name_for_new_sites() {
1994 return __( 'WooCommerce Tax', 'woocommerce-services' );
1995 }
1996
1997 private function get_plugin_description_for_new_sites() {
1998 return __( 'Automated tax calculation for WooCommerce.', 'woocommerce-services' );
1999 }
2000
2001 /**
2002 * Adds the Sift JS page tracker if needed.
2003 *
2004 * @return void
2005 */
2006 public function add_sift_js_tracker() {
2007 $sift_configurations = $this->api_client->get_sift_configuration();
2008
2009 if ( is_wp_error( $sift_configurations ) ) {
2010 $this->schedule_background_sift_fetch();
2011 return;
2012 }
2013
2014 $connected_data = WC_Connect_Jetpack::get_connection_owner_wpcom_data();
2015
2016 if ( empty( $sift_configurations->beacon_key ) || empty( $connected_data['ID'] ) ) {
2017 return;
2018 }
2019
2020 wp_register_script(
2021 'sift-science',
2022 'https://cdn.sift.com/s.js',
2023 array(),
2024 null,
2025 array(
2026 'strategy' => 'defer',
2027 'in_footer' => true,
2028 )
2029 );
2030
2031 $inline_script = sprintf(
2032 'var _sift = window._sift = window._sift || [];' .
2033 '_sift.push(["_setAccount", %s]);' .
2034 '_sift.push(["_setUserId", %s]);' .
2035 '_sift.push(["_trackPageview"]);',
2036 wp_json_encode( $sift_configurations->beacon_key ),
2037 wp_json_encode( $connected_data['ID'] )
2038 );
2039
2040 wp_add_inline_script( 'sift-science', $inline_script, 'after' );
2041
2042 wp_enqueue_script( 'sift-science' );
2043 }
2044
2045 /**
2046 * Schedule a background fetch for Sift configuration.
2047 *
2048 * @return void
2049 */
2050 private function schedule_background_sift_fetch() {
2051 if ( get_transient( self::SIFT_FETCH_IN_PROGRESS_TRANSIENT_KEY ) ) {
2052 return;
2053 }
2054
2055 set_transient( self::SIFT_FETCH_IN_PROGRESS_TRANSIENT_KEY, true, 5 * MINUTE_IN_SECONDS );
2056
2057 if ( ! wp_next_scheduled( 'wc_connect_fetch_sift_config' ) ) {
2058 wp_schedule_single_event( time() + 1, 'wc_connect_fetch_sift_config' );
2059 }
2060 }
2061
2062 /**
2063 * Fetch Sift configuration in the background.
2064 *
2065 * @return void
2066 */
2067 public function background_fetch_sift_config() {
2068 $config = $this->api_client->get_sift_configuration( true );
2069
2070 if ( ! is_wp_error( $config ) ) {
2071 delete_transient( self::SIFT_FETCH_IN_PROGRESS_TRANSIENT_KEY );
2072 }
2073 }
2074
2075 public function enqueue_wc_connect_script( $root_view, $extra_args = array() ) {
2076 $is_alive = $this->api_client->is_alive_cached();
2077
2078 $account_settings = new WC_Connect_Account_Settings(
2079 $this->service_settings_store,
2080 $this->payment_methods_store
2081 );
2082 $packages_settings = new WC_Connect_Package_Settings(
2083 $this->service_settings_store,
2084 $this->service_schemas_store
2085 );
2086 $payload = array(
2087 'nonce' => wp_create_nonce( 'wp_rest' ),
2088 'baseURL' => get_rest_url(),
2089 'wcs_server_connection' => $is_alive,
2090 'accountSettings' => $account_settings->get(),
2091 'packagesSettings' => $packages_settings->get(),
2092 'is_wcshipping_active' => self::is_wc_shipping_activated(),
2093 );
2094
2095 wp_localize_script( 'wc_connect_admin', 'wcConnectData', $payload );
2096 wp_enqueue_script( 'wc_connect_admin' );
2097
2098 $debug_page_uri = add_query_arg(
2099 array(
2100 'page' => 'wc-status',
2101 'tab' => 'connect',
2102 ),
2103 admin_url( 'admin.php' )
2104 );
2105
2106 $encoded_arguments = wp_json_encode( $extra_args );
2107 ?>
2108 <div class="wcc-root woocommerce <?php echo esc_attr( $root_view ); ?>" data-args="<?php echo wc_esc_json( $encoded_arguments ); ?>">
2109 <span class="form-troubles" style="opacity: 0">
2110 <?php printf( esc_html__( 'Section not loading? Visit the <a href="%s">status page</a> for troubleshooting steps.', 'woocommerce-services' ), esc_url( $debug_page_uri ) ); ?>
2111 </span>
2112 </div>
2113 <?php
2114 }
2115
2116 public function render_schema_notices() {
2117 $schemas = $this->get_service_schemas_store()->get_service_schemas();
2118 if ( empty( $schemas ) || ! property_exists( $schemas, 'notices' ) || empty( $schemas->notices ) ) {
2119 return;
2120 }
2121 $allowed_html = array(
2122 'a' => array( 'href' => array() ),
2123 'strong' => array(),
2124 'br' => array(),
2125 );
2126 foreach ( $schemas->notices as $notice ) {
2127 $dismissible = false;
2128 // check if the notice is dismissible.
2129 if ( property_exists( $notice, 'id' ) && ! empty( $notice->id ) && property_exists( $notice, 'dismissible' ) && $notice->dismissible ) {
2130 // check if the notice is being dismissed right now.
2131 if ( isset( $_GET['wc-connect-dismiss-server-notice'] ) && $_GET['wc-connect-dismiss-server-notice'] === $notice->id ) {
2132 set_transient( 'wcc_notice_dismissed_' . $notice->id, true, MONTH_IN_SECONDS );
2133 continue;
2134 }
2135 // check if the notice has already been dismissed.
2136 if ( false !== get_transient( 'wcc_notice_dismissed_' . $notice->id ) ) {
2137 continue;
2138 }
2139
2140 $dismissible = true;
2141 $link_dismiss = add_query_arg( array( 'wc-connect-dismiss-server-notice' => $notice->id ) );
2142 }
2143 ?>
2144 <div class='<?php echo esc_attr( 'notice notice-' . $notice->type ); ?>' style="position: relative;">
2145 <?php if ( $dismissible ) : ?>
2146 <a href="<?php echo esc_url( $link_dismiss ); ?>" style="text-decoration: none;" class="notice-dismiss" title="<?php esc_attr_e( 'Dismiss this notice', 'woocommerce-services' ); ?>"></a>
2147 <?php endif; ?>
2148 <p><?php echo wp_kses( $notice->message, $allowed_html ); ?></p>
2149 </div>
2150 <?php
2151 }
2152 }
2153
2154 /**
2155 * Check if WooCommerce Shipping has been activated.
2156 *
2157 * @return bool
2158 */
2159 public static function is_wc_shipping_activated() {
2160 return in_array( 'woocommerce-shipping/woocommerce-shipping.php', get_option( 'active_plugins' ) );
2161 }
2162
2163 /**
2164 * Returns if both Woo Shipping and Woo Tax are active.
2165 *
2166 * @return bool
2167 */
2168 public function are_woo_shipping_and_woo_tax_active() {
2169 $is_woo_tax_active = in_array( 'woocommerce-tax/woocommerce-tax.php', get_option( 'active_plugins' ) );
2170
2171 return self::is_wc_shipping_activated() && $is_woo_tax_active;
2172 }
2173
2174 /**
2175 * Echoes an admin notice informing of Woo Shipping and Woo Tax being active.
2176 *
2177 * To be used in the `admin_notices` hook.
2178 *
2179 * @return void
2180 */
2181 public function display_woo_shipping_and_woo_tax_are_active_notice() {
2182 echo '<div class="error"><p><strong>' . esc_html__( 'WC Shipping and WC Tax plugins are already active. Please deactivate WooCommerce Tax.', 'woocommerce-services' ) . '</strong></p></div>';
2183 }
2184
2185 /**
2186 * Returns the notice for migrating from WCST to WC Shipping.
2187 *
2188 * @return bool
2189 */
2190 public function display_wcst_to_wcshipping_migration_notice() {
2191 if ( isset( $_COOKIE[ self::MIGRATION_DISMISSAL_COOKIE_KEY ] ) && (int) $_COOKIE[ self::MIGRATION_DISMISSAL_COOKIE_KEY ] === 1 ) {
2192 return;
2193 }
2194 $schema = $this->get_service_schemas_store();
2195 $banner = $schema->get_wcship_wctax_upgrade_banner();
2196 if ( empty( $banner ) ) {
2197 return;
2198 }
2199
2200 $account_settings = new WC_Connect_Account_Settings(
2201 $this->service_settings_store,
2202 $this->payment_methods_store
2203 );
2204 $packages_settings = new WC_Connect_Package_Settings(
2205 $this->service_settings_store,
2206 $this->service_schemas_store
2207 );
2208 $encoded_arguments = wp_json_encode(
2209 array(
2210 'nonce' => wp_create_nonce( 'wp_rest' ),
2211 'baseURL' => get_rest_url(),
2212 'accountSettings' => $account_settings->get(),
2213 'packagesSettings' => $packages_settings->get(),
2214 )
2215 );
2216
2217 echo wp_kses_post(
2218 sprintf(
2219 '<div class="notice notice-%s wcst-wcshipping-migration-notice">
2220 <div class="wcst-wcshipping-migration-notice__content">
2221 <div id="wcst_wcshipping_migration_admin_notice_feature_announcement" data-args="%s"></div>
2222 <p>',
2223 $banner->type,
2224 wc_esc_json( $encoded_arguments )
2225 ) .
2226 sprintf(
2227 /* translators: %s: documentation URL */
2228 __( $banner->message, 'woocommerce-services' ),
2229 'https://woocommerce.com/document/woocommerce-shipping-and-tax/woocommerce-shipping/#how-do-i-migrate-from-wcst'
2230 ) .
2231 sprintf(
2232 '</p>
2233 <button type="button" class="notice-dismiss %s"><span class="screen-reader-text">Dismiss this notice.</span></button>
2234 </div>
2235 <div class="notice-action"><button id="wcst-wcshipping-migration-notice__click" type="button" class="action-button">%s</button></div>
2236 </div>',
2237 $banner->dismissible ? '' : 'hide-dismissible',
2238 $banner->action
2239 )
2240 );
2241 }
2242
2243 public function register_wcshipping_migration_modal() {
2244 $plugin_version = self::get_wcs_version();
2245 wp_register_style( 'wcst_wcshipping_migration_admin_notice', $this->wc_connect_base_url . 'woocommerce-services-wcshipping-migration-admin-notice-' . $plugin_version . '.css', array(), null ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
2246 wp_register_script( 'wcst_wcshipping_migration_admin_notice', $this->wc_connect_base_url . 'woocommerce-services-wcshipping-migration-admin-notice-' . $plugin_version . '.js', array(), null );
2247 wp_localize_script(
2248 'wcst_wcshipping_migration_admin_notice',
2249 'wcsPluginData',
2250 array(
2251 'assetPath' => self::get_wc_connect_base_url(),
2252 'adminPluginPath' => admin_url( 'plugins.php' ),
2253 )
2254 );
2255 wp_enqueue_script( 'wc_connect_admin' );
2256 wp_enqueue_script( 'wcst_wcshipping_migration_admin_notice' );
2257 wp_enqueue_style( 'wcst_wcshipping_migration_admin_notice' );
2258 }
2259 }
2260 }
2261
2262 register_deactivation_hook( __FILE__, array( 'WC_Connect_Loader', 'plugin_deactivation' ) );
2263 register_uninstall_hook( __FILE__, array( 'WC_Connect_Loader', 'plugin_uninstall' ) );
2264
2265 // Jetpack's Rest_Authentication needs to be initialized even before plugins_loaded.
2266 Automattic\Jetpack\Connection\Rest_Authentication::init();
2267
2268 if ( ! defined( 'WC_UNIT_TESTING' ) ) {
2269 new WC_Connect_Loader();
2270 }
2271