*/ class Polkurier { /** * The loader that's responsible for maintaining and registering all hooks that power * the plugin. * * @since 1.0.0 * @access protected * @var Polkurier_Loader $loader Maintains and registers all hooks for the plugin. */ protected $loader; /** * The unique identifier of this plugin. * * @since 1.0.0 * @access protected * @var string $plugin_name The string used to uniquely identify this plugin. */ protected $plugin_name; /** * The current version of the plugin. * * @since 1.0.0 * @access protected * @var string $version The current version of the plugin. */ protected $version; /** * @var Polkurier */ private static $runtimeInstance; /** * @var string */ private $pluginDir; /** * @var string */ private $pluginUrl; /** * @var string */ private $text_domain = 'woocommerce-polkurier'; /** * @var string[] */ private $shippingMethods = [ 'pk_inpost_paczkomat' => InpostParcelMachineShippingMethodMethod::class, 'pk_paczka_w_ruchu' => PaczkaWRuchuShippingMethodMethod::class, 'pk_dpd_point' => DpdShippingMethodMethod::class, 'pk_fedex_dtr' => FedExDtrShippingMethodMethod::class, 'pk_dhl_point' => DhlPopShippingMethodMethod::class, 'pk_ups_access_point' => UpsAccessPointShippingMethodMethod::class, 'pk_pocztex_point' => PocztexPointShippingMethod::class, ]; /** * @var AbstractIntegration[] */ private $integrations = []; /** * Define the core functionality of the plugin. * * Set the plugin name and the plugin version that can be used throughout the plugin. * Load the dependencies, define the locale, and set the hooks for the admin area and * the public-facing side of the site. * * @since 1.0.0 */ public function __construct() { $this->version = POLKURIER_PLUGIN_VERSION; $this->plugin_name = 'polkurier'; $this->pluginDir = plugin_dir_path(__DIR__); $this->pluginUrl = plugin_dir_url(__DIR__); $this->load_dependencies(); $this->set_locale(); $this->define_admin_hooks(); $this->define_public_hooks(); add_action('plugins_loaded', [$this, 'onPluginsLoaded'], 100); add_action('wp_ajax_pk_courier_points', function () { $this->handlerAjaxRequest(function (Request $request) { return (new CourierPointController())->searchAction($request); }); }); add_action('wp_ajax_pk_update_default_courier_point', function () { $this->handlerAjaxRequest(function (Request $request) { return (new CourierPointController())->setCourierPoint($request); }); }); add_action('wp_ajax_pk_evaluate_order', function () { $this->handlerAjaxRequest(function (Request $request) { return (new OrderController())->evaluateAction($request); }); }); // Rejestruje integrację z WC Blocks add_action('woocommerce_blocks_checkout_block_registration', function ($integration_registry) { $integration_registry->register(new WooCommerceCheckoutIntegration()); }); // Rejestruje zapis zmian na froncie add_action('woocommerce_blocks_loaded', function () { woocommerce_store_api_register_update_callback([ 'namespace' => $this->get_plugin_name(), 'callback' => function ($data) { (new CheckoutController())->handleStoreUpdateCallback( (string)Arr::get($data, 'action'), new Request((array)Arr::get($data, 'data')) ); } ]); }); } /** * @param callable $action */ public function handlerAjaxRequest(callable $action) { $nonce = Arr::get($_POST, 'nonce', Arr::get($_GET, 'nonce')); if (!wp_verify_nonce($nonce, 'polkurier-nonce')) { $response = new JsonResponse(['error' => 'Forbidden'], 403); $response->send(); exit; } try { $reponse = $action(new Request()); if (!$reponse instanceof Response) { throw new HttpException('Controller must return instance of Response class', 500); } } catch (HttpException $e) { $reponse = new JsonResponse(['error' => $e->getMessage()], $e->getCode()); } catch (\Exception $e) { $reponse = new JsonResponse(['error' => $e->getMessage()], 500); } $reponse->send(); exit; } /** * @return Polkurier */ public static function instance() { if (static::$runtimeInstance === null) { static::$runtimeInstance = new Polkurier(); } return static::$runtimeInstance; } /** * @return string */ public function getPluginDir() { return $this->pluginDir; } /** * @param $path * @return string */ public function getPluginPath($path) { return $this->getPluginDir() . '/' . $path; } /** * @return string */ public function getPluginUrl() { return $this->pluginUrl; } /** * @return string */ public function getTextDomain() { return $this->text_domain; } /** * Zawraca URL do katalogu bazowego * @param $file * @return string */ public function getPluginDirUrl($file) { $basePath = untrailingslashit(plugin_dir_url(__FILE__)); $lastPart = strrpos($basePath, '/'); if ($lastPart !== false) { $basePath = substr($basePath, 0, $lastPart); } return trailingslashit($basePath) . $file; } /** * Load the required dependencies for this plugin. * * Include the following files that make up the plugin: * * - Polkurier_Loader. Orchestrates the hooks of the plugin. * - Polkurier_i18n. Defines internationalization functionality. * - Polkurier_Admin. Defines all hooks for the admin area. * - Polkurier_Public. Defines all hooks for the public side of the site. * * Create an instance of the loader which will be used to register the hooks * with WordPress. * * @since 1.0.0 * @access private */ private function load_dependencies() { /** * The class responsible for orchestrating the actions and filters of the * core plugin. */ require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-polkurier-loader.php'; /** * The class responsible for defining internationalization functionality * of the plugin. */ require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-polkurier-i18n.php'; /** * The class responsible for defining all actions that occur in the admin area. */ require_once plugin_dir_path(dirname(__FILE__)) . 'admin/class-polkurier-admin.php'; /** * The class responsible for defining all actions that occur in the public-facing * side of the site. */ require_once plugin_dir_path(dirname(__FILE__)) . 'public/class-polkurier-public.php'; require_once plugin_dir_path(dirname(__FILE__)) . 'includes/PolkurierOrderStatusUpdater.php'; $this->loader = new Polkurier_Loader(); } /** * Define the locale for this plugin for internationalization. * * Uses the Polkurier_i18n class in order to set the domain and to register the hook * with WordPress. * * @since 1.0.0 * @access private */ private function set_locale() { $plugin_i18n = new Polkurier_i18n(); $this->loader->add_action('plugins_loaded', $plugin_i18n, 'load_plugin_textdomain'); } /** * Register all of the hooks related to the admin area functionality * of the plugin. * * @since 1.0.0 * @access private */ private function define_admin_hooks() { $plugin_admin = new Polkurier_Admin($this->get_plugin_name(), $this->get_version()); $this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_styles'); $this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts'); // Hooks into admin_menu hook to add custom page $this->loader->add_action('admin_menu', $plugin_admin, 'add_admin_page'); $this->loader->add_action('admin_init', $plugin_admin, 'register_setting'); } /** * Register all of the hooks related to the public-facing functionality * of the plugin. * * @since 1.0.0 * @access private */ private function define_public_hooks() { $plugin_public = new Polkurier_Public( $this->get_plugin_name(), $this->get_version() ); // $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_styles' ); // $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_scripts' ); // Hook in add_filter('woocommerce_checkout_fields', 'polkurier_override_checkout_fields'); // Our hooked in function - $fields is passed via the filter! function polkurier_override_checkout_fields($fields) { $fields['shipping']['shipping_phone'] = array( 'label' => __('Telefon', 'woocommerce'), 'placeholder' => _x('Telefon', 'placeholder', 'woocommerce'), 'required' => false, 'class' => array('form-row-wide'), 'clear' => true ); $fields['shipping']['shipping_email'] = array( 'label' => __('E-mail', 'woocommerce'), 'placeholder' => _x('E-mail', 'placeholder', 'woocommerce'), 'required' => false, 'class' => array('form-row-wide'), 'clear' => true ); return $fields; } /** * Display field value on the order edit page */ add_action('woocommerce_admin_order_data_after_shipping_address', 'polkurier_checkout_field_display_admin_order_meta', 10, 1); /** * @param WC_Order $order */ function polkurier_checkout_field_display_admin_order_meta($order) { echo '

' . __('Numer telefonu') . ': ' . get_post_meta($order->get_id(), '_shipping_phone', true) . '

'; echo '

' . __('Adres e-mail') . ': ' . get_post_meta($order->get_id(), '_shipping_email', true) . '

'; echo '

' . __('Punkt odbioru') . ': ' . get_post_meta($order->get_id(), '_polkurier_point_id', true) . '

'; $shippingMethod = OrderTools::getShippingMethod($order); if (!empty(get_post_meta($order->get_id(), '_polkurier_point_id', true))) { echo '

' . '' . ($shippingMethod !== null && OrderTools::isOwnShippingMethod($shippingMethod) ? $shippingMethod->getSelectedPointLabel() : 'Wybrany punkt odbioru') . ':
' . '' . get_post_meta($order->get_id(), '_polkurier_point_id', true) . '
' . nl2br(get_post_meta($order->get_id(), '_polkurier_point_label', true)) . '

'; } } } /** * @return array */ public function getShippingMethods() { $shippingMethods = []; foreach ($this->getAllShippingMethods() as $shippingMethod) { if ($shippingMethod instanceof AbstractShippingMethod) { $shippingMethods[] = $shippingMethod; } } return $shippingMethods; } /** * @param $id * @param null $instanceId * @return WC_Shipping_Method|null */ public function getShippingMethodById($id, $instanceId = null) { foreach ($this->getAllShippingMethods() as $shippingMethod) { if ($shippingMethod->id === $id && ($instanceId === null || $instanceId === $shippingMethod->instance_id)) { return $shippingMethod; } } return null; } /** * @return WC_Shipping_Method[] */ public function getAllShippingMethods() { $zones = (array)WC_Shipping_Zones::get_zones(); $shippingMethods = []; foreach ($zones as $zone) { /** @var WC_Shipping_Method $method */ foreach ((array)$zone['shipping_methods'] as $method) { $shippingMethods[] = $method; } } return $shippingMethods; } public function getMapShippingMethods() { $methods = []; foreach ($this->getAllShippingMethods() as $shippingMethod) { if ($shippingMethod instanceof AbstractShippingMethod && $shippingMethod->isParcelPickupPoint()) { $methods[] = $shippingMethod; } foreach ($this->integrations as $integration) { if ($integration instanceof FlexibleShipping && $integration->shouldShowMapButton($shippingMethod)) { $methods[] = $shippingMethod; } } } return $methods; } public function getMapShippingMethodsIds() { $methodsIds = []; foreach ($this->getMapShippingMethods() as $shippingMethod) { $methodsIds[] = $shippingMethod->id . ':' . $shippingMethod->instance_id; } return $methodsIds; } /** * @return WC_Shipping_Method|null */ public function getCurrentShippingMethod() { $session = WC()->session; if ($session === null) { return null; } $chosenShippingMethods = $session->chosen_shipping_methods; if (!is_array($chosenShippingMethods) || count($chosenShippingMethods) === 0) { return null; } $currentShippingMethodId = $chosenShippingMethods[0]; foreach ($this->getAllShippingMethods() as $method) { $methodId = $method->id . ':' . $method->instance_id; if ($methodId === $currentShippingMethodId) { return $method; } } return null; } /** * @return AbstractIntegration[] */ public function getIntegrations() { return $this->integrations; } public function onPluginsLoaded() { if (!class_exists('WooCommerce') || version_compare(WC()->version, '3.0', '<')) { add_action('admin_notices', function () { echo '

Polkurier wymaga do działania wtyczki WooCommerce w wersji minimum 3.0

'; }); return; } $activePlugins = apply_filters('active_plugins', get_option('active_plugins')); // Integracja z FlexibleShipping if (in_array('flexible-shipping/flexible-shipping.php', $activePlugins, true)) { $this->integrations[] = new FlexibleShipping(); } add_filter('woocommerce_shipping_methods', [$this, 'onWoocommerceShippingMethods']); add_filter('woocommerce_get_order_item_totals', [$this, 'onWoocommerceGetOrderItemTotals'], 2, 100); add_action('wp_enqueue_scripts', function () { if (is_checkout()) { $this->enqueueFrontAssets(); } }); // Formatowanie adresu wysyłki po złożeniu zamówienia add_filter('woocommerce_order_get_formatted_shipping_address', function ($addressData) { global $wp; $orderId = (int)Arr::get($wp->query_vars, 'order-received'); if ($orderId > 0) { $order = new WC_Order($orderId); $orderShippingMethod = Arr::get(array_values($order->get_shipping_methods()), '0'); $shippingMethod = \Polkurier::instance()->getShippingMethodById($orderShippingMethod->get_method_id(), (int)$orderShippingMethod->get_instance_id()); if ($shippingMethod !== null) { $shouldShowPickupPointAddress = false; if ($shippingMethod instanceof AbstractShippingMethod && $shippingMethod->isParcelPickupPoint()) { $shouldShowPickupPointAddress = true; } foreach ($this->integrations as $integration) { if ($integration instanceof FlexibleShipping && $integration->shouldShowMapButton($shippingMethod)) { $shouldShowPickupPointAddress = true; } } if ($shouldShowPickupPointAddress) { return wc_get_template_html( 'checkout/order_get_formatted_shipping_address.php', [ 'provider' => get_post_meta($orderId, '_polkurier_provider', true), 'pointId' => get_post_meta($orderId, '_polkurier_point_id', true), 'pointLabel' => get_post_meta($orderId, '_polkurier_point_label', true), 'pointType' => get_post_meta($orderId, '_polkurier_point_type', true), ], '', $this->getPluginPath('templates/') ); } } } return $addressData; }); $renderMapButton = function ($method, $position) { if (is_checkout() && get_option('polkurier_layout_checkout_point_button_position') === $position) { $currentMethod = \Polkurier::instance()->getCurrentShippingMethod(); if ($method instanceof \WC_Shipping_Rate && $currentMethod !== null) { $isValid = $method->id === $currentMethod->id . ':' . $currentMethod->get_instance_id(); } else { $isValid = true; } if ($currentMethod !== null && $isValid) { if ($currentMethod instanceof AbstractShippingMethod && $currentMethod->isParcelPickupPoint()) { $currentMethod->render(); } foreach ($this->integrations as $integration) { if ($integration instanceof FlexibleShipping && $integration->shouldShowMapButton($currentMethod)) { $integration->renderShippingMethod($currentMethod); } } } } }; // Wyświetla przycisk "Wybierz paczkomat" pod blokiem wyboru metod wysyłki add_action('woocommerce_review_order_after_shipping', function () use ($renderMapButton) { $renderMapButton(null, CheckoutPointButtonPosition::AFTER_SHIPPING_BLOCK); }); // Wyświetla przycisk "Wybierz paczkomat" w wybranym miejscu foreach (CheckoutPointButtonPosition::VALID_POSITIONS as $positionName) { $filterName = $positionName; if (!empty(CheckoutPointButtonPosition::MAP_POSITIONS[$positionName])) { $filterName = CheckoutPointButtonPosition::MAP_POSITIONS[$positionName]; } $priority = CheckoutPointButtonPosition::DEFAULT_PRIORITY; if (!empty(CheckoutPointButtonPosition::PRIORITIES[$positionName])) { $priority = CheckoutPointButtonPosition::PRIORITIES[$positionName]; } add_action($filterName, function ($method) use ($renderMapButton, $positionName) { $renderMapButton($method, $positionName); }, $priority); } add_action('woocommerce_checkout_update_order_review', function () { $currentMethod = \Polkurier::instance()->getCurrentShippingMethod(); if ($currentMethod instanceof AbstractShippingMethod) { $currentMethod->onCheckoutUpdateOrderReview(); } }); // Walidacja zamówienia legacy add_action('woocommerce_checkout_process', function () { $currentMethod = \Polkurier::instance()->getCurrentShippingMethod(); if ($currentMethod instanceof AbstractShippingMethod && $currentMethod->isParcelPickupPoint()) { $currentMethod->onCheckoutProcess(); } foreach ($this->integrations as $integration) { if ($integration instanceof FlexibleShipping) { $integration->onCheckoutProcess(); } } }); // Walidacja zamówienia Blocks add_action('woocommerce_store_api_checkout_order_processed', function (WC_Order $order) { $currentMethod = \Polkurier::instance()->getCurrentShippingMethod(); if ($currentMethod instanceof AbstractShippingMethod && $currentMethod->isParcelPickupPoint()) { $currentMethod->onCheckoutProcess($order); } foreach ($this->integrations as $integration) { if ($integration instanceof FlexibleShipping) { $integration->onCheckoutProcess($order); } } }, 10, 3); // Zapis zamówienia add_action('woocommerce_new_order', function ($post_id) { if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return; } $sessionStore = WC()->session->get($this->get_plugin_name() . '_delivery_point'); $hasSessionStore = !empty($sessionStore); $pointId = Arr::get($_POST, 'polkurier_point_id'); if ($hasSessionStore && empty($pointId)) { $pointId = Arr::get($sessionStore, 'id'); } if (empty($pointId) && !empty($post_id)) { $pointId = Arr::get(get_post_meta($post_id, '_polkurier_point_id'), 0); } $providerName = Arr::get($_POST, 'polkurier_provider'); if ($hasSessionStore && empty($providerName)) { $providerName = Arr::get($sessionStore, 'provider'); } if (empty($providerName) && !empty($post_id)) { $providerName = Arr::get(get_post_meta($post_id, '_polkurier_provider'), 0); } $pointLabel = Arr::get($_POST, 'polkurier_point_label'); if ($hasSessionStore && empty($pointLabel)) { $pointLabel = Arr::get($sessionStore, 'label'); } if (empty($pointLabel) && !empty($post_id)) { $pointLabel = Arr::get(get_post_meta($post_id, '_polkurier_point_label'), 0); } $pointType = Arr::get($_POST, 'polkurier_point_type'); if ($hasSessionStore && empty($pointType)) { $pointType = Arr::get($sessionStore, 'type'); } if (empty($pointType) && !empty($post_id)) { $pointType = Arr::get(get_post_meta($post_id, '_polkurier_point_type'), 0); } update_post_meta($post_id, '_polkurier_provider', esc_attr($providerName)); update_post_meta($post_id, '_polkurier_point_id', esc_attr($pointId)); update_post_meta($post_id, '_polkurier_point_label', esc_attr($pointLabel)); update_post_meta($post_id, '_polkurier_point_type', esc_attr($pointType)); WC()->session->set(\Polkurier::instance()->get_plugin_name() . '_delivery_point', null); }); } public function onWoocommerceGetOrderItemTotals($items, $wcOrder) { $pointId = get_post_meta($wcOrder->get_id(), '_polkurier_point_id', true); if (isset($items['shipping']) && !empty($pointId)) { $items['shipping']['value'] .= wc_get_template_html( 'checkout/get_order_item_totals_point.php', [ 'pointId' => $pointId, 'pointLabel' => get_post_meta($wcOrder->get_id(), '_polkurier_point_label', true), ], '', $this->getPluginPath('templates/') ); } return $items; } public function enqueueFrontAssets() { $name = $this->get_plugin_name(); wp_enqueue_script(uniqid($name, true), $this->getPluginUrl() . 'public/js/polkurier-checkout-page.js', ['jquery'], $this->version, true); wp_enqueue_script(uniqid($name, true), 'https://maps.polkurier.pl/assets/dist/points-map.bundle.js', ['jquery'], $this->version, true); wp_enqueue_style(uniqid($name, true), 'https://maps.polkurier.pl/assets/dist/points-map.css', [], $this->version); } /** * @param array $methods * @return array */ public function onWoocommerceShippingMethods($methods) { foreach ($this->shippingMethods as $methodId => $methodClass) { $methods[$methodId] = $methodClass; } return $methods; } /** * Run the loader to execute all of the hooks with WordPress. * * @since 1.0.0 */ public function run() { $this->loader->run(); } /** * The name of the plugin used to uniquely identify it within the context of * WordPress and to define internationalization functionality. * * @return string The name of the plugin. * @since 1.0.0 */ public function get_plugin_name() { return $this->plugin_name; } /** * The reference to the class that orchestrates the hooks with the plugin. * * @return Polkurier_Loader Orchestrates the hooks of the plugin. * @since 1.0.0 */ public function get_loader() { return $this->loader; } /** * Retrieve the version number of the plugin. * * @return string The version number of the plugin. * @since 1.0.0 */ public function get_version() { return $this->version; } }