| 1 |
<?php |
| 2 |
/** |
| 3 |
* Class Main file. |
| 4 |
* |
| 5 |
* @package PostNLWooCommerce |
| 6 |
*/ |
| 7 |
|
| 8 |
namespace PostNLWooCommerce; |
| 9 |
|
| 10 |
if ( ! defined( 'ABSPATH' ) ) { |
| 11 |
exit; |
| 12 |
} |
| 13 |
|
| 14 |
use PostNLWooCommerce\Product\Product_Editor; |
| 15 |
use PostNLWooCommerce\Checkout_Blocks\Extend_Block_Core; |
| 16 |
use PostNLWooCommerce\Checkout_Blocks\Blocks_Integration; |
| 17 |
use PostNLWooCommerce\Checkout_Blocks\Extend_Store_Endpoint; |
| 18 |
use PostNLWooCommerce\Shipping_Method\Fill_In_With_PostNL_Settings; |
| 19 |
|
| 20 |
/** |
| 21 |
* Class Main |
| 22 |
* |
| 23 |
* @package PostNLWooCommerce |
| 24 |
*/ |
| 25 |
class Main { |
| 26 |
/** |
| 27 |
* Version of this plugin. |
| 28 |
* |
| 29 |
* @var _version |
| 30 |
*/ |
| 31 |
private $version = '5.9.11'; |
| 32 |
|
| 33 |
/** |
| 34 |
* The ID of this plugin settings. |
| 35 |
* |
| 36 |
* @var settings_id |
| 37 |
*/ |
| 38 |
public $settings_id = 'postnl'; |
| 39 |
|
| 40 |
/** |
| 41 |
* The name of this plugin service. |
| 42 |
* |
| 43 |
* @var service_name |
| 44 |
*/ |
| 45 |
public $service_name = 'PostNL'; |
| 46 |
|
| 47 |
/** |
| 48 |
* Shipping Product. |
| 49 |
* |
| 50 |
* @var PostNLWooCommerce\Product\PostNL |
| 51 |
*/ |
| 52 |
public $shipping_product = null; |
| 53 |
|
| 54 |
/** |
| 55 |
* Shipping Order. |
| 56 |
* |
| 57 |
* @var PostNLWooCommerce\Order\Single |
| 58 |
*/ |
| 59 |
public $shipping_order = null; |
| 60 |
|
| 61 |
/** |
| 62 |
* Shipping Order Bulk. |
| 63 |
* |
| 64 |
* @var PostNLWooCommerce\Order\Bulk |
| 65 |
*/ |
| 66 |
public $shipping_order_bulk = null; |
| 67 |
|
| 68 |
/** |
| 69 |
* Orders List |
| 70 |
* |
| 71 |
* @var PostNLWooCommerce\Order\OrdersList |
| 72 |
*/ |
| 73 |
public $orders_list = null; |
| 74 |
|
| 75 |
/** |
| 76 |
* Shipping Settings. |
| 77 |
* |
| 78 |
* @var PostNLWooCommerce\Shipping_Method\Settings |
| 79 |
*/ |
| 80 |
public $shipping_settings = null; |
| 81 |
|
| 82 |
|
| 83 |
/** |
| 84 |
* Instance to call certain functions globally within the plugin |
| 85 |
* |
| 86 |
* @var _instance |
| 87 |
*/ |
| 88 |
protected static $instance = null; |
| 89 |
|
| 90 |
/** |
| 91 |
* Product Editor. |
| 92 |
* |
| 93 |
* @var PostNLWooCommerce\Product\Product_Editor |
| 94 |
*/ |
| 95 |
public $product_editor = null; |
| 96 |
|
| 97 |
/** |
| 98 |
* Construct the plugin. |
| 99 |
*/ |
| 100 |
public function __construct() { |
| 101 |
$this->define_constants(); |
| 102 |
// Throw an admin error informing the user this plugin needs WooCommerce to function. |
| 103 |
add_action( 'admin_notices', array( $this, 'notice_wc_required' ) ); |
| 104 |
|
| 105 |
if ( ! class_exists( 'WooCommerce' ) ) { |
| 106 |
return; |
| 107 |
} |
| 108 |
|
| 109 |
// Declare WooCommerce features compatibility. |
| 110 |
add_action( 'before_woocommerce_init', array( $this, 'declare_wc_hpos_compatibility' ) ); |
| 111 |
add_action( 'before_woocommerce_init', array( $this, 'declare_product_editor_compatibility' ) ); |
| 112 |
|
| 113 |
// Throw an admin error informing the user this plugin needs country settings to be NL and BE. |
| 114 |
add_action( 'admin_notices', array( $this, 'notice_nl_be_required' ) ); |
| 115 |
|
| 116 |
if ( ! Utils::use_available_country() ) { |
| 117 |
return; |
| 118 |
} |
| 119 |
|
| 120 |
add_action( 'init', array( $this, 'load_plugin' ), 1 ); |
| 121 |
add_filter( 'woocommerce_shipping_methods', array( $this, 'add_shipping_method' ) ); |
| 122 |
} |
| 123 |
|
| 124 |
/** |
| 125 |
* Declare WooCommerce HPOS feature compatibility. |
| 126 |
*/ |
| 127 |
public function declare_wc_hpos_compatibility() { |
| 128 |
if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) { |
| 129 |
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', POSTNL_WC_PLUGIN_BASENAME, true ); |
| 130 |
} |
| 131 |
} |
| 132 |
|
| 133 |
/** |
| 134 |
* Declare Product Editor compatibility. |
| 135 |
*/ |
| 136 |
public function declare_product_editor_compatibility() { |
| 137 |
if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) { |
| 138 |
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'product_block_editor', POSTNL_WC_PLUGIN_BASENAME, true ); |
| 139 |
} |
| 140 |
} |
| 141 |
|
| 142 |
/** |
| 143 |
* Main PostNL for WooCommerce. |
| 144 |
* |
| 145 |
* Ensures only one instance is loaded or can be loaded. |
| 146 |
* |
| 147 |
* @static |
| 148 |
* @return self Main instance. |
| 149 |
*/ |
| 150 |
public static function instance() { |
| 151 |
if ( is_null( self::$instance ) ) { |
| 152 |
self::$instance = new self(); |
| 153 |
} |
| 154 |
return self::$instance; |
| 155 |
} |
| 156 |
|
| 157 |
/** |
| 158 |
* Define WC Constants. |
| 159 |
*/ |
| 160 |
private function define_constants() { |
| 161 |
$upload_dir = wp_upload_dir(); |
| 162 |
|
| 163 |
// Path related defines. |
| 164 |
$this->define( 'POSTNL_WC_PLUGIN_FILE', POSTNL_WC_PLUGIN_FILE ); |
| 165 |
$this->define( 'POSTNL_WC_PLUGIN_DIR_PATH', untrailingslashit( plugin_dir_path( POSTNL_WC_PLUGIN_FILE ) ) ); |
| 166 |
$this->define( 'POSTNL_WC_PLUGIN_DIR_URL', untrailingslashit( plugins_url( '/', POSTNL_WC_PLUGIN_FILE ) ) ); |
| 167 |
|
| 168 |
$this->define( 'POSTNL_WC_SANDBOX_API_URL', esc_url( 'https://api-sandbox.postnl.nl' ) ); |
| 169 |
$this->define( 'POSTNL_WC_PROD_API_URL', esc_url( 'https://api.postnl.nl' ) ); |
| 170 |
|
| 171 |
$this->define( 'POSTNL_WC_VERSION', $this->version ); |
| 172 |
$this->define( 'POSTNL_SETTINGS_ID', $this->settings_id ); |
| 173 |
$this->define( 'POSTNL_SERVICE_NAME', $this->service_name ); |
| 174 |
$this->define( 'POSTNL_WC_LOG_DIR', $upload_dir['basedir'] . '/wc-logs/' ); |
| 175 |
$this->define( 'POSTNL_UPLOADS_DIR', $upload_dir['basedir'] . '/postnl/' ); |
| 176 |
} |
| 177 |
|
| 178 |
/** |
| 179 |
* Determine which plugin to load. |
| 180 |
*/ |
| 181 |
public function load_plugin() { |
| 182 |
$this->init_hooks(); |
| 183 |
$this->checkout_blocks(); |
| 184 |
$this->maybe_revert_discontinued_return_option(); |
| 185 |
} |
| 186 |
|
| 187 |
/** |
| 188 |
* Switch the discontinued "Shipment & Return" return option back to "None". |
| 189 |
* |
| 190 |
* PostNL discontinues the Shipment & Return Label product on 1 July 2026. |
| 191 |
* Stores that still have it selected are reverted to "None" so outbound |
| 192 |
* label generation does not fail against the API. Runs once per install. |
| 193 |
* |
| 194 |
* @since 5.9.7 |
| 195 |
*/ |
| 196 |
public function maybe_revert_discontinued_return_option() { |
| 197 |
if ( 'done' === get_option( 'postnl_shipping_return_reverted' ) ) { |
| 198 |
return; |
| 199 |
} |
| 200 |
|
| 201 |
$settings = get_option( 'woocommerce_postnl_settings', array() ); |
| 202 |
$reverted = true; |
| 203 |
|
| 204 |
if ( is_array( $settings ) && 'shipping_return' === ( $settings['return_shipment_and_labels'] ?? null ) ) { |
| 205 |
$settings['return_shipment_and_labels'] = 'none'; |
| 206 |
$reverted = update_option( 'woocommerce_postnl_settings', $settings ); |
| 207 |
} |
| 208 |
|
| 209 |
// Only mark the migration complete when nothing needed reverting or the |
| 210 |
// revert write succeeded, so a failed write retries on the next request. |
| 211 |
if ( $reverted ) { |
| 212 |
update_option( 'postnl_shipping_return_reverted', 'done' ); |
| 213 |
} |
| 214 |
} |
| 215 |
|
| 216 |
/** |
| 217 |
* Initialize the plugin. |
| 218 |
*/ |
| 219 |
public function init() { |
| 220 |
$this->get_shipping_order(); |
| 221 |
$this->get_shipping_order_bulk(); |
| 222 |
$this->get_orders_list(); |
| 223 |
$this->get_shipping_product(); |
| 224 |
$this->load_fill_in_with_postnl_settings(); |
| 225 |
$this->get_frontend(); |
| 226 |
$this->get_product_editor(); |
| 227 |
} |
| 228 |
|
| 229 |
/** |
| 230 |
* Collection of hooks. |
| 231 |
*/ |
| 232 |
public function init_hooks() { |
| 233 |
add_action( 'init', array( $this, 'init' ), 5 ); |
| 234 |
add_action( 'init', array( $this, 'load_textdomain' ) ); |
| 235 |
|
| 236 |
// Locate woocommerce template. |
| 237 |
add_filter( 'woocommerce_locate_template', array( $this, 'woocommerce_locate_template' ), 20, 3 ); |
| 238 |
|
| 239 |
add_filter( 'woocommerce_email_classes', array( $this, 'add_wc_smart_return_email' ) ); |
| 240 |
|
| 241 |
// Register the block category. |
| 242 |
add_action( 'block_categories_all', array( $this, 'register_postnl_block_category' ), 10, 2 ); |
| 243 |
|
| 244 |
add_filter( 'plugin_row_meta', array( $this, 'add_row_meta' ), 10, 2 ); |
| 245 |
add_filter( 'plugin_action_links_' . POSTNL_WC_PLUGIN_BASENAME, array( $this, 'add_action_links' ), 10, 1 ); |
| 246 |
} |
| 247 |
|
| 248 |
/** |
| 249 |
* Add the smart return email class. |
| 250 |
* |
| 251 |
* @param array $email_classes Array of existing WC emails. |
| 252 |
* |
| 253 |
* @return array $email_classes |
| 254 |
*/ |
| 255 |
public function add_wc_smart_return_email( $email_classes ) { |
| 256 |
// Add the smart return email to the list of email classes. |
| 257 |
$email_classes['WC_Smart_Return_Email'] = new Emails\WC_Email_Smart_Return(); |
| 258 |
|
| 259 |
return $email_classes; |
| 260 |
} |
| 261 |
|
| 262 |
/** |
| 263 |
* Localisation. |
| 264 |
*/ |
| 265 |
public function load_textdomain() { |
| 266 |
load_plugin_textdomain( 'postnl-for-woocommerce', false, untrailingslashit( dirname( POSTNL_WC_PLUGIN_BASENAME ) ) . '/languages' ); |
| 267 |
} |
| 268 |
|
| 269 |
/** |
| 270 |
* Add PostNL Shipping Method to WooCommerce. |
| 271 |
* |
| 272 |
* @param array<WC_Shipping_Method> $shipping_methods Array of existing WC shipping methods. |
| 273 |
* |
| 274 |
* @return array<WC_Shipping_Method> |
| 275 |
*/ |
| 276 |
public function add_shipping_method( $shipping_methods ) { |
| 277 |
$shipping_methods[ $this->settings_id ] = 'PostNLWooCommerce\Shipping_Method\PostNL'; |
| 278 |
return $shipping_methods; |
| 279 |
} |
| 280 |
|
| 281 |
/** |
| 282 |
* Get order single class. |
| 283 |
* |
| 284 |
* @return Order\Single |
| 285 |
*/ |
| 286 |
public function get_shipping_order() { |
| 287 |
if ( empty( $this->shipping_order ) ) { |
| 288 |
$this->shipping_order = new Order\Single(); |
| 289 |
} |
| 290 |
|
| 291 |
return $this->shipping_order; |
| 292 |
} |
| 293 |
|
| 294 |
/** |
| 295 |
* Get product editor class. |
| 296 |
* |
| 297 |
* @return Product\Product_Editor |
| 298 |
*/ |
| 299 |
public function get_product_editor() { |
| 300 |
if ( empty( $this->product_editor ) ) { |
| 301 |
$this->product_editor = new Product\Product_Editor(); |
| 302 |
} |
| 303 |
|
| 304 |
return $this->product_editor; |
| 305 |
} |
| 306 |
|
| 307 |
/** |
| 308 |
* Get order bulk class. |
| 309 |
* |
| 310 |
* @return Order\Bulk |
| 311 |
*/ |
| 312 |
public function get_shipping_order_bulk() { |
| 313 |
if ( empty( $this->shipping_order_bulk ) ) { |
| 314 |
$this->shipping_order_bulk = new Order\Bulk(); |
| 315 |
} |
| 316 |
|
| 317 |
return $this->shipping_order_bulk; |
| 318 |
} |
| 319 |
|
| 320 |
/** |
| 321 |
* Get order bulk class. |
| 322 |
* |
| 323 |
* @return Order\OrdersList |
| 324 |
*/ |
| 325 |
public function get_orders_list() { |
| 326 |
if ( empty( $this->orders_list ) ) { |
| 327 |
$this->shipping_order_bulk = new Order\OrdersList(); |
| 328 |
} |
| 329 |
|
| 330 |
return $this->orders_list; |
| 331 |
} |
| 332 |
|
| 333 |
/** |
| 334 |
* Get product class. |
| 335 |
* |
| 336 |
* @return Product\Single |
| 337 |
*/ |
| 338 |
public function get_shipping_product() { |
| 339 |
if ( empty( $this->shipping_product ) ) { |
| 340 |
$this->shipping_product = new Product\Single(); |
| 341 |
} |
| 342 |
|
| 343 |
return $this->shipping_product; |
| 344 |
} |
| 345 |
|
| 346 |
/** |
| 347 |
* Get frontend class. |
| 348 |
*/ |
| 349 |
public function get_frontend() { |
| 350 |
new Frontend\Container(); |
| 351 |
new Frontend\Delivery_Day(); |
| 352 |
new Frontend\Dropoff_Points(); |
| 353 |
new Frontend\Checkout_Fields(); |
| 354 |
new Frontend\Fill_In_With_Postnl(); |
| 355 |
new Frontend\Fill_In_With_Postnl_Handler(); |
| 356 |
} |
| 357 |
|
| 358 |
/** |
| 359 |
* Get settings class. |
| 360 |
* |
| 361 |
* @return Shipping_Method\Settings |
| 362 |
*/ |
| 363 |
public function get_shipping_settings() { |
| 364 |
if ( empty( $this->shipping_settings ) ) { |
| 365 |
$this->shipping_settings = new Shipping_Method\Settings(); |
| 366 |
} |
| 367 |
|
| 368 |
return $this->shipping_settings; |
| 369 |
} |
| 370 |
|
| 371 |
public function load_fill_in_with_postnl_settings() { |
| 372 |
if ( class_exists( 'PostNLWooCommerce\Shipping_Method\Fill_In_With_PostNL_Settings' ) ) { |
| 373 |
new Fill_In_With_PostNL_Settings(); |
| 374 |
} |
| 375 |
} |
| 376 |
|
| 377 |
/** |
| 378 |
* Define constant if not already set. |
| 379 |
* |
| 380 |
* @param string $name Name of constant variable. |
| 381 |
* @param string|bool $value Value of constant variable. |
| 382 |
*/ |
| 383 |
public function define( $name, $value ) { |
| 384 |
if ( ! defined( $name ) ) { |
| 385 |
define( $name, $value ); |
| 386 |
} |
| 387 |
} |
| 388 |
|
| 389 |
/** |
| 390 |
* Admin error notifying user that WC is required. |
| 391 |
*/ |
| 392 |
public function notice_wc_required() { |
| 393 |
if ( ! class_exists( 'WooCommerce' ) ) { |
| 394 |
?> |
| 395 |
<div class="error"> |
| 396 |
<p><?php esc_html_e( 'PostNL plugin requires WooCommerce to be installed and activated!', 'postnl-for-woocommerce' ); ?></p> |
| 397 |
</div> |
| 398 |
<?php |
| 399 |
} |
| 400 |
} |
| 401 |
|
| 402 |
/** |
| 403 |
* Admin error notifying user that Country must be using Netherlands or Belgium. |
| 404 |
*/ |
| 405 |
public function notice_nl_be_required() { |
| 406 |
if ( ! Utils::use_available_country() ) { |
| 407 |
?> |
| 408 |
<div class="error"> |
| 409 |
<p><?php esc_html_e( 'PostNL plugin requires store country to be Netherlands (NL) or Belgium (BE)!', 'postnl-for-woocommerce' ); ?></p> |
| 410 |
</div> |
| 411 |
<?php |
| 412 |
} |
| 413 |
} |
| 414 |
|
| 415 |
/** |
| 416 |
* Manipulate the WooCommerce template file location. |
| 417 |
* |
| 418 |
* @param string $template Template filename before manipulated. |
| 419 |
* @param string $template_name Template filename to be manipulated. |
| 420 |
* @param string $template_path Template new path. |
| 421 |
* |
| 422 |
* @return String |
| 423 |
*/ |
| 424 |
public function woocommerce_locate_template( $template, $template_name, $template_path ) { |
| 425 |
|
| 426 |
global $woocommerce; |
| 427 |
|
| 428 |
$_template = $template; |
| 429 |
|
| 430 |
if ( ! $template_path ) { |
| 431 |
$template_path = $woocommerce->template_url; |
| 432 |
} |
| 433 |
|
| 434 |
$plugin_path = untrailingslashit( POSTNL_WC_PLUGIN_DIR_PATH ) . '/templates/'; |
| 435 |
|
| 436 |
// Look within passed path within the theme - this is priority. |
| 437 |
$template = locate_template( |
| 438 |
array( |
| 439 |
$template_path . $template_name, |
| 440 |
$template_name, |
| 441 |
) |
| 442 |
); |
| 443 |
|
| 444 |
// Modification: Get the template from this plugin, if it exists. |
| 445 |
if ( ! $template && file_exists( $plugin_path . $template_name ) ) { |
| 446 |
$template = $plugin_path . $template_name; |
| 447 |
} |
| 448 |
|
| 449 |
// Use default template. |
| 450 |
|
| 451 |
if ( ! $template ) { |
| 452 |
$template = $_template; |
| 453 |
} |
| 454 |
|
| 455 |
// Return what we found. |
| 456 |
return $template; |
| 457 |
} |
| 458 |
|
| 459 |
public function checkout_blocks() { |
| 460 |
if ( ! Utils::is_blocks_checkout() ) { |
| 461 |
return; |
| 462 |
} |
| 463 |
|
| 464 |
// Initialize classes that depend on WooCommerce |
| 465 |
new Extend_Block_Core(); |
| 466 |
Extend_Store_Endpoint::init(); |
| 467 |
// Register the blocks integration |
| 468 |
add_action( |
| 469 |
'woocommerce_blocks_checkout_block_registration', |
| 470 |
function ( $integration_registry ) { |
| 471 |
$integration_registry->register( new Blocks_Integration() ); |
| 472 |
} |
| 473 |
); |
| 474 |
} |
| 475 |
/** |
| 476 |
* Registers the slug as a block category with WordPress. |
| 477 |
* |
| 478 |
* @param array $categories Existing categories. |
| 479 |
* @return array Modified categories. |
| 480 |
*/ |
| 481 |
public function register_postnl_block_category( $categories ) { |
| 482 |
return array_merge( |
| 483 |
$categories, |
| 484 |
array( |
| 485 |
array( |
| 486 |
'slug' => 'postnl', |
| 487 |
'title' => __( 'Postnl Checkout Blocks', 'postnl-for-woocommerce' ), |
| 488 |
), |
| 489 |
) |
| 490 |
); |
| 491 |
} |
| 492 |
|
| 493 |
/** |
| 494 |
* Add row meta links. |
| 495 |
* |
| 496 |
* @param string[] $links Existing links. |
| 497 |
* @param string $file Plugin file name. |
| 498 |
* |
| 499 |
* @return string[] |
| 500 |
*/ |
| 501 |
public function add_row_meta( array $links, string $file ): array { |
| 502 |
if ( $file === POSTNL_WC_PLUGIN_BASENAME ) { |
| 503 |
$links[] = sprintf( |
| 504 |
'<a href="%s" target="_blank" rel="noopener">%s</a>', |
| 505 |
esc_url( 'https://wordpress.org/support/plugin/woo-postnl/reviews/#new-post' ), |
| 506 |
esc_html__( 'Leave a review', 'postnl-for-woocommerce' ) |
| 507 |
); |
| 508 |
} |
| 509 |
|
| 510 |
return $links; |
| 511 |
} |
| 512 |
|
| 513 |
/** |
| 514 |
* Add action links. |
| 515 |
* |
| 516 |
* @param string[] $links Existing links. |
| 517 |
* |
| 518 |
* @return string[] |
| 519 |
*/ |
| 520 |
public function add_action_links( array $links ): array { |
| 521 |
array_unshift( |
| 522 |
$links, |
| 523 |
sprintf( |
| 524 |
'<a href="%s">%s</a>', |
| 525 |
esc_url( admin_url( 'admin.php?page=wc-settings&tab=shipping§ion=postnl' ) ), |
| 526 |
esc_html__( 'Settings', 'postnl-for-woocommerce' ) |
| 527 |
) |
| 528 |
); |
| 529 |
|
| 530 |
return $links; |
| 531 |
} |
| 532 |
|
| 533 |
/** |
| 534 |
* Get logger object. |
| 535 |
*/ |
| 536 |
public static function get_logger() { |
| 537 |
$settings = Shipping_Method\Settings::get_instance(); |
| 538 |
return new Logger( $settings->is_logging_enabled() ); |
| 539 |
} |
| 540 |
} |
| 541 |
|