PluginProbe
Packeta / 2.1
Packeta v2.1
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / src / Packetery / Module / Options / Page.php

Page.php in Packeta 2.1, at src/Packetery/Module/Options/Page.php

1,033 lines 38.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Page
4 *
5 * @package Packetery\Options
6 */
7
8 declare( strict_types=1 );
9
10 namespace Packetery\Module\Options;
11
12 use DateTime;
13 use Packetery\Core\Api;
14 use Packetery\Core\Api\Soap\Request\SenderGetReturnRouting;
15 use Packetery\Core\CoreHelper;
16 use Packetery\Core\Entity\PacketStatus;
17 use Packetery\Core\Log;
18 use Packetery\Latte\Engine;
19 use Packetery\Module\Dashboard\DashboardPage;
20 use Packetery\Module\FormFactory;
21 use Packetery\Module\Framework\WpAdapter;
22 use Packetery\Module\MessageManager;
23 use Packetery\Module\ModuleHelper;
24 use Packetery\Module\Order\PacketAutoSubmitter;
25 use Packetery\Module\Order\PacketSynchronizer;
26 use Packetery\Module\PaymentGatewayHelper;
27 use Packetery\Module\Views\UrlBuilder;
28 use Packetery\Nette\Forms\Container;
29 use Packetery\Nette\Forms\Controls\BaseControl;
30 use Packetery\Nette\Forms\Controls\SubmitButton;
31 use Packetery\Nette\Forms\Form;
32 use Packetery\Nette\Http;
33
34 /**
35 * Class Page
36 *
37 * @package Packetery\Options
38 */
39 class Page {
40
41 private const FORM_FIELDS_CONTAINER = 'packetery';
42 private const FORM_FIELD_PACKETA_LABEL_FORMAT = 'packeta_label_format';
43 private const FORM_FIELD_CARRIER_LABEL_FORMAT = 'carrier_label_format';
44 private const FORM_FIELD_FREE_SHIPPING_SHOWN = 'free_shipping_shown';
45
46 public const ACTION_VALIDATE_SENDER = 'validate-sender';
47
48 public const SLUG = 'packeta-options';
49
50 public const TAB_GENERAL = 'general';
51 public const TAB_ADVANCED = 'advanced';
52 public const TAB_SUPPORT = 'support';
53 public const TAB_PACKET_STATUS_SYNC = 'packet-status-sync';
54 public const TAB_AUTO_SUBMISSION = 'auto-submission';
55
56 public const PARAM_TAB = 'tab';
57
58 const PACKETA_SVG_LOGO = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IlZyc3R2YV8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIKCSB2aWV3Qm94PSIwIDAgMzcgNDAiIHN0eWxlPSJmaWxsOiNhN2FhYWQiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiAjYTdhYWFkO30KPC9zdHlsZT4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTE5LjQsMTYuMWwtMC45LDAuNGwtMC45LTAuNGwtMTMtNi41bDYuMi0yLjRsMTMuNCw2LjVMMTkuNCwxNi4xeiBNMzIuNSw5LjZsLTQuNywyLjNsLTEzLjUtNmw0LjItMS42CglMMzIuNSw5LjZ6Ii8+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xOSwwbDE3LjIsNi42bC0yLjQsMS45bC0xNS4yLTZMMy4yLDguNkwwLjgsNi42TDE4LDBMMTksMEwxOSwweiBNMzQuMSw5LjFsMi44LTEuMWwtMi4xLDE3LjZsLTAuNCwwLjgKCUwxOS40LDQwbC0wLjUtMy4xbDEzLjQtMTJMMzQuMSw5LjF6IE0yLjUsMjYuNWwtMC40LTAuOEwwLDguMWwyLjgsMS4xbDEuOSwxNS43bDEzLjQsMTJMMTcuNiw0MEwyLjUsMjYuNXoiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTI4LjIsMTIuNGw0LjMtMi43bC0xLjcsMTQuMkwxOC42LDM1bDAuNi0xN2w1LjQtMy4zTDI0LjMsMjNsMy4zLTIuM0wyOC4yLDEyLjR6Ii8+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xNy43LDE3LjlsMC42LDE3bC0xMi4yLTExTDQuNCw5LjhMMTcuNywxNy45eiIvPgo8L3N2Zz4K';
59
60 /**
61 * PacketeryLatte_engine.
62 *
63 * @var Engine PacketeryLatte engine.
64 */
65 private $latteEngine;
66
67 /**
68 * Options Provider
69 *
70 * @var OptionsProvider
71 */
72 private $optionsProvider;
73
74 /**
75 * Form factory.
76 *
77 * @var FormFactory Form factory.
78 */
79 private $formFactory;
80
81 /**
82 * Packeta client.
83 *
84 * @var Api\Soap\Client
85 */
86 private $packetaClient;
87
88 /**
89 * Logger
90 *
91 * @var Log\ILogger
92 */
93 private $logger;
94
95 /**
96 * Message manager.
97 *
98 * @var MessageManager
99 */
100 private $messageManager;
101
102 /**
103 * @var Http\Request
104 */
105 private $httpRequest;
106
107 /**
108 * @var ModuleHelper
109 */
110 private $moduleHelper;
111
112 /**
113 * @var UrlBuilder
114 */
115 private $urlBuilder;
116
117 /**
118 * @var PacketSynchronizer
119 */
120 private $packetSynchronizer;
121
122 /**
123 * @var WpAdapter
124 */
125 private $wpAdapter;
126
127 /**
128 * @var string
129 */
130 private $supportEmailAddress;
131
132 public function __construct(
133 Engine $latteEngine,
134 OptionsProvider $optionsProvider,
135 FormFactory $formFactory,
136 Api\Soap\Client $packetaClient,
137 Log\ILogger $logger,
138 MessageManager $messageManager,
139 Http\Request $httpRequest,
140 ModuleHelper $moduleHelper,
141 UrlBuilder $urlBuilder,
142 PacketSynchronizer $packetSynchronizer,
143 WpAdapter $wpAdapter,
144 string $supportEmailAddress
145 ) {
146 $this->latteEngine = $latteEngine;
147 $this->optionsProvider = $optionsProvider;
148 $this->formFactory = $formFactory;
149 $this->packetaClient = $packetaClient;
150 $this->logger = $logger;
151 $this->messageManager = $messageManager;
152 $this->httpRequest = $httpRequest;
153 $this->moduleHelper = $moduleHelper;
154 $this->urlBuilder = $urlBuilder;
155 $this->packetSynchronizer = $packetSynchronizer;
156 $this->supportEmailAddress = $supportEmailAddress;
157 $this->wpAdapter = $wpAdapter;
158 }
159
160 public function register(): void {
161 add_action( 'admin_init', [ $this, 'admin_init' ] );
162 add_action( 'admin_init', [ $this, 'processActions' ] );
163 add_filter( 'custom_menu_order', '__return_true' );
164 add_filter( 'menu_order', [ $this, 'customMenuOrder' ] );
165 }
166
167 public function registerMenuPage(): void {
168 $this->wpAdapter->addMenuPage(
169 $this->wpAdapter->__( 'Packeta', 'packeta' ),
170 $this->wpAdapter->__( 'Packeta', 'packeta' ),
171 'manage_options',
172 DashboardPage::SLUG,
173 function () {},
174 self::PACKETA_SVG_LOGO
175 );
176 }
177
178 public function registerSubmenuPage(): void {
179 $this->wpAdapter->addSubmenuPage(
180 DashboardPage::SLUG,
181 $this->wpAdapter->__( 'Settings', 'packeta' ),
182 $this->wpAdapter->__( 'Settings', 'packeta' ),
183 'manage_options',
184 self::SLUG,
185 array(
186 $this,
187 'render',
188 ),
189 1
190 );
191 }
192
193 /**
194 * Returns menu_order with Packeta item in last position.
195 *
196 * @param array $menuOrder WP $menu_order.
197 *
198 * @return array
199 */
200 public function customMenuOrder( array $menuOrder ): array {
201 $currentPosition = array_search( self::SLUG, $menuOrder, true );
202
203 if ( $currentPosition !== false ) {
204 unset( $menuOrder[ $currentPosition ] );
205 $menuOrder[] = self::SLUG;
206 }
207
208 return $menuOrder;
209 }
210
211 /**
212 * Get chosen keys by hashed choice data.
213 *
214 * @param array $choiceData Choice data.
215 * @param array $chosenHashedFormOrderStatuses Picked items.
216 *
217 * @return array
218 */
219 private function getChosenKeys( array $choiceData, array $chosenHashedFormOrderStatuses ): array {
220 $statuses = [];
221 foreach ( $chosenHashedFormOrderStatuses as $hash => $chosen ) {
222 if ( $chosen ) {
223 $statuses[] = $choiceData[ $hash ]['key'];
224 }
225 }
226
227 return $statuses;
228 }
229
230 /**
231 * Translate hashes to packet status keys.
232 *
233 * @param array $choiceData Choice data.
234 * @param array $formData Picked items.
235 *
236 * @return array
237 */
238 private function translateStatuses( array $choiceData, array $formData ): array {
239 $statuses = [];
240 foreach ( $formData as $hash => $value ) {
241 if ( $value ) {
242 $statuses[ $choiceData[ $hash ]['key'] ] = $value;
243 }
244 }
245
246 return $statuses;
247 }
248
249 /**
250 * Gets status syncing packet statuses.
251 *
252 * @return array<string, array{key: int|string, default: bool, label:string}>
253 */
254 public function getStatusSyncingPacketStatusesChoiceData(): array {
255 $statuses = $this->packetSynchronizer->getDefaultPacketStatuses();
256
257 return $this->createPacketStatusChoiceData( $statuses );
258 }
259
260 /**
261 * Gets all packet statuses.
262 *
263 * @return array<string, array{key: int|string, default: bool, label:string}>
264 */
265 public function getAllPacketStatusesChoiceData(): array {
266 $statuses = $this->packetSynchronizer->getPacketStatuses();
267
268 return $this->createPacketStatusChoiceData( $statuses );
269 }
270
271 /**
272 * @param PacketStatus[] $statuses Packet statuses.
273 *
274 * @return array<string, array{key: int|string, default: bool, label:string}>
275 */
276 private function createPacketStatusChoiceData( array $statuses ): array {
277 $result = [];
278
279 foreach ( $statuses as $status => $statusEntity ) {
280 $result[ md5( $status ) ] = [
281 'key' => $status,
282 'default' => $statusEntity->hasDefaultSynchronization(),
283 'label' => $statusEntity->getTranslatedName(),
284 ];
285 }
286
287 return $result;
288 }
289
290 /**
291 * Gets order statuses for form.
292 *
293 * @return array<string, array<string, int|string>>
294 */
295 public static function getOrderStatusesChoiceData(): array {
296 $orderStatuses = wc_get_order_statuses();
297 $orderStatusesTransformed = [];
298
299 foreach ( $orderStatuses as $orderStatusKey => $orderStatusLabel ) {
300 $orderStatusesTransformed[ md5( $orderStatusKey ) ] = [
301 'key' => $orderStatusKey,
302 'label' => $orderStatusLabel,
303 ];
304 }
305
306 return $orderStatusesTransformed;
307 }
308
309 /**
310 * Creates auto submission form.
311 *
312 * @return Form
313 */
314 public function createAutoSubmissionForm(): Form {
315 $gateways = PaymentGatewayHelper::getAvailablePaymentGateways();
316 $form = $this->formFactory->create( 'packetery_auto_submission_form' );
317 $defaults = $this->optionsProvider->getOptionsByName( OptionNames::PACKETERY_AUTO_SUBMISSION );
318
319 $form->addCheckbox( 'allow', __( 'Allow packet auto-submission', 'packeta' ) )
320 ->setRequired( false )
321 ->setDefaultValue( OptionsProvider::PACKET_AUTO_SUBMISSION_ALLOWED_DEFAULT );
322
323 $eventChoices = [
324 PacketAutoSubmitter::EVENT_ON_ORDER_CREATION_FE => __( 'On order creation at e-shop checkout', 'packeta' ),
325 PacketAutoSubmitter::EVENT_ON_ORDER_PROCESSING => __( 'On order processing', 'packeta' ),
326 PacketAutoSubmitter::EVENT_ON_ORDER_COMPLETED => __( 'On order completed', 'packeta' ),
327 ];
328
329 $paymentMethodEvents = $form->addContainer( 'payment_method_events' );
330 foreach ( $gateways as $gateway ) {
331 $paymentMethodEventsMethod = $paymentMethodEvents->addContainer(
332 $this->optionsProvider->sanitizePaymentGatewayId( $gateway->id )
333 );
334 $paymentMethodEventsMethod->addSelect( 'event', $gateway->get_method_title(), $eventChoices )
335 ->setPrompt( __( 'Select event', 'packeta' ) )
336 ->checkDefaultValue( false );
337 }
338
339 $form->addSubmit( 'save', __( 'Save changes', 'packeta' ) );
340
341 $form->setDefaults( $defaults );
342
343 $form->onSuccess[] = [ $this, 'onAutoSubmissionFormSuccess' ];
344
345 return $form;
346 }
347
348 /**
349 * On auto submission form success.
350 *
351 * @param Form $form Form.
352 * @param array $values Form values.
353 *
354 * @return void
355 */
356 public function onAutoSubmissionFormSuccess( Form $form, array $values ): void {
357 update_option( OptionNames::PACKETERY_AUTO_SUBMISSION, $values );
358
359 $this->messageManager->flash_message( __( 'Settings saved.', 'packeta' ), MessageManager::TYPE_SUCCESS, MessageManager::RENDERER_PACKETERY, 'plugin-options' );
360
361 if ( wp_safe_redirect( $this->createLink( self::TAB_AUTO_SUBMISSION ) ) ) {
362 exit;
363 }
364 }
365
366 /**
367 * Creates settings form.
368 *
369 * @return Form
370 */
371 private function createPacketStatusSyncForm(): Form {
372 $form = $this->formFactory->create( 'packetery_packet_status_sync_form' );
373 $settings = $this->optionsProvider->getOptionsByName( OptionNames::PACKETERY_SYNC );
374
375 $form->addText( 'max_status_syncing_packets', __( 'Number of orders synced during one cron call', 'packeta' ) )
376 ->setRequired( false )
377 ->addRule( Form::INTEGER )
378 ->addRule( Form::MIN, null, 0 )
379 ->setDefaultValue( OptionsProvider::MAX_STATUS_SYNCING_PACKETS_DEFAULT );
380
381 $form->addText( 'max_days_of_packet_status_syncing', __( 'Number of days for which the order status is checked', 'packeta' ) )
382 ->setRequired( false )
383 ->addRule( Form::INTEGER )
384 ->addRule( Form::MIN, null, 0 )
385 ->setDefaultValue( OptionsProvider::MAX_DAYS_OF_PACKET_STATUS_SYNCING_DEFAULT );
386
387 $orderStatusesTransformed = self::getOrderStatusesChoiceData();
388 $orderStatusesContainer = $form->addContainer( 'status_syncing_order_statuses' );
389
390 foreach ( $orderStatusesTransformed as $orderStatusKeyHash => $orderStatusData ) {
391 $item = $orderStatusesContainer->addCheckbox( $orderStatusKeyHash, $orderStatusData['label'] );
392 if ( isset( $settings['status_syncing_order_statuses'] ) && in_array( $orderStatusData['key'], $settings['status_syncing_order_statuses'], true ) ) {
393 $item->setDefaultValue( true );
394 }
395 }
396 unset( $settings['status_syncing_order_statuses'] );
397
398 $packetStatuses = $this->getStatusSyncingPacketStatusesChoiceData();
399 $packetStatusesContainer = $form->addContainer( 'status_syncing_packet_statuses' );
400
401 foreach ( $packetStatuses as $packetStatusHash => $packetStatusData ) {
402 $item = $packetStatusesContainer->addCheckbox( $packetStatusHash, $packetStatusData['label'] );
403 if ( ! isset( $settings['status_syncing_packet_statuses'] ) ) {
404 $item->setDefaultValue( $packetStatusData['default'] );
405 } elseif ( in_array( $packetStatusData['key'], $settings['status_syncing_packet_statuses'], true ) ) {
406 $item->setDefaultValue( true );
407 }
408 }
409 unset( $settings['status_syncing_packet_statuses'] );
410
411 $form->addCheckbox( 'allow_order_status_change', __( 'Allow order status change', 'packeta' ) )
412 ->setRequired( false )
413 ->setDefaultValue( false )
414 ->addCondition( Form::EQUAL, true )
415 ->toggle( '.order_status_change_content' );
416
417 $orderStatusChangePacketStatuses = $form->addContainer( 'order_status_change_packet_statuses' );
418 $orderStatuses = wc_get_order_statuses();
419 $packetStatuses = $this->getAllPacketStatusesChoiceData();
420 foreach ( $packetStatuses as $packetStatusHash => $packetStatusData ) {
421 $item = $orderStatusChangePacketStatuses->addSelect( $packetStatusHash, $packetStatusData['label'], $orderStatuses )
422 ->setPrompt( __( 'Order status', 'packeta' ) );
423 $targetStatus = $settings['order_status_change_packet_statuses'][ $packetStatusData['key'] ] ?? null;
424 if ( $targetStatus !== null && array_key_exists( $targetStatus, $orderStatuses ) ) {
425 $item->setDefaultValue( $targetStatus );
426 }
427 }
428 unset( $settings['order_status_change_packet_statuses'] );
429
430 $form->setDefaults( $settings );
431
432 $form->addSubmit( 'save', __( 'Save changes', 'packeta' ) );
433
434 $form->onSuccess[] = [ $this, 'onPacketStatusSyncFormSuccess' ];
435
436 return $form;
437 }
438
439 /**
440 * On packet status sync form success.
441 *
442 * @param Form $form Form.
443 * @param array $values Values.
444 *
445 * @return void
446 */
447 public function onPacketStatusSyncFormSuccess( Form $form, array $values ): void {
448
449 $values['status_syncing_order_statuses'] = $this->getChosenKeys(
450 self::getOrderStatusesChoiceData(),
451 $values['status_syncing_order_statuses']
452 );
453 $values['status_syncing_packet_statuses'] = $this->getChosenKeys(
454 $this->getStatusSyncingPacketStatusesChoiceData(),
455 $values['status_syncing_packet_statuses']
456 );
457 $values['order_status_change_packet_statuses'] = $this->translateStatuses(
458 $this->getAllPacketStatusesChoiceData(),
459 $values['order_status_change_packet_statuses']
460 );
461
462 if ( $values['max_status_syncing_packets'] === '' ) {
463 unset( $values['max_status_syncing_packets'] );
464 }
465
466 if ( $values['max_days_of_packet_status_syncing'] === '' ) {
467 unset( $values['max_days_of_packet_status_syncing'] );
468 }
469
470 update_option( OptionNames::PACKETERY_SYNC, $values );
471
472 $this->messageManager->flash_message( __( 'Settings saved.', 'packeta' ), MessageManager::TYPE_SUCCESS, MessageManager::RENDERER_PACKETERY, 'plugin-options' );
473
474 if ( wp_safe_redirect( $this->createLink( self::TAB_PACKET_STATUS_SYNC ) ) ) {
475 exit;
476 }
477 }
478
479 public function createAdvancedForm(): Form {
480 $form = $this->formFactory->create( 'packetery_advanced_form' );
481 $defaults = $this->optionsProvider->getOptionsByName( OptionNames::PACKETERY_ADVANCED );
482
483 $form->addCheckbox( 'new_carrier_settings_enabled', __( 'Advanced carrier settings', 'packeta' ) )
484 ->setRequired( false )
485 ->setDefaultValue( OptionsProvider::DEFAULT_VALUE_CARRIER_SETTINGS );
486
487 $form->addSubmit( 'save', __( 'Save changes', 'packeta' ) );
488
489 $form->setDefaults( $defaults );
490
491 $form->onSuccess[] = [ $this, 'onAdvancedFormSuccess' ];
492
493 return $form;
494 }
495
496 public function onAdvancedFormSuccess( Form $form, array $values ): void {
497 update_option( OptionNames::PACKETERY_ADVANCED, $values );
498
499 $this->messageManager->flash_message( __( 'Settings saved.', 'packeta' ), MessageManager::TYPE_SUCCESS, MessageManager::RENDERER_PACKETERY, 'plugin-options' );
500
501 if ( wp_safe_redirect( $this->createLink( self::TAB_ADVANCED ) ) ) {
502 exit;
503 }
504 }
505
506 /**
507 * Creates settings form.
508 *
509 * @return Form
510 */
511 public function create_form(): Form {
512 $form = $this->formFactory->create();
513 $form->setAction( 'options.php' );
514
515 $container = $form->addContainer( self::FORM_FIELDS_CONTAINER );
516 $container->addText( 'api_password', __( 'API password', 'packeta' ) )
517 ->setRequired()
518 ->addRule( $form::PATTERN, __( 'API password must be 32 characters long and must contain valid characters!', 'packeta' ), '[a-z\d]{32}' );
519 $container->addText( 'sender', __( 'Sender', 'packeta' ) )
520 ->setRequired();
521
522 $packetaLabelFormats = $this->optionsProvider->getPacketaLabelFormats();
523 $container->addSelect(
524 self::FORM_FIELD_PACKETA_LABEL_FORMAT,
525 __( 'Packeta Label Format', 'packeta' ),
526 $packetaLabelFormats
527 )->checkDefaultValue( false )->setDefaultValue( OptionsProvider::DEFAULT_VALUE_PACKETA_LABEL_FORMAT );
528
529 $carrierLabelFormats = $this->optionsProvider->getCarrierLabelFormat();
530 $container->addSelect(
531 self::FORM_FIELD_CARRIER_LABEL_FORMAT,
532 __( 'Carrier Label Format', 'packeta' ),
533 $carrierLabelFormats
534 )->checkDefaultValue( false )->setDefaultValue( OptionsProvider::DEFAULT_VALUE_CARRIER_LABEL_FORMAT );
535
536 $gateways = PaymentGatewayHelper::getAvailablePaymentGateways();
537 $enabledGateways = [];
538 foreach ( $gateways as $gateway ) {
539 $enabledGateways[ $gateway->id ] = $gateway->get_method_title();
540 }
541 $container->addMultiSelect(
542 'cod_payment_methods',
543 __( 'Payment methods that represent cash on delivery', 'packeta' ),
544 $enabledGateways
545 )->checkDefaultValue( false );
546
547 $container->addText( 'packaging_weight', __( 'Weight of packaging material', 'packeta' ) . ' (kg)' )
548 ->setRequired()
549 ->addRule( Form::FLOAT )
550 ->addRule( Form::MIN, null, 0 )
551 ->setDefaultValue( 0 );
552
553 $container->addCheckbox( 'default_weight_enabled', __( 'Enable default weight', 'packeta' ) )
554 ->addCondition( Form::EQUAL, true )
555 ->toggle( '#packetery-default-weight-value' );
556
557 $container->addText( 'default_weight', __( 'Default weight', 'packeta' ) . ' (kg)' )
558 ->addConditionOn( $form[ self::FORM_FIELDS_CONTAINER ]['default_weight_enabled'], Form::EQUAL, true )
559 ->setRequired()
560 ->addRule( Form::FLOAT )
561 ->addRule( Form::MIN, null, 0.1 );
562
563 $container->addSelect(
564 'dimensions_unit',
565 __( 'Units used for dimensions', 'packeta' ),
566 [
567 OptionsProvider::DIMENSIONS_UNIT_CM => 'cm',
568 OptionsProvider::DEFAULT_DIMENSIONS_UNIT_MM => 'mm',
569 ]
570 )
571 ->setDefaultValue( $this->optionsProvider::DEFAULT_DIMENSIONS_UNIT_MM );
572
573 $container->addCheckbox( 'default_dimensions_enabled', __( 'Enable default dimensions', 'packeta' ) )
574 ->addCondition( Form::EQUAL, true )
575 ->toggle( '#packetery-default-dimensions-value' );
576
577 $container->addText( 'default_length', __( 'Length', 'packeta' ) )
578 ->addConditionOn( $form[ self::FORM_FIELDS_CONTAINER ]['default_dimensions_enabled'], Form::EQUAL, true )
579 ->setRequired()
580 ->addConditionOn( $form[ self::FORM_FIELDS_CONTAINER ]['dimensions_unit'], Form::EQUAL, $this->optionsProvider::DEFAULT_DIMENSIONS_UNIT_MM )
581 ->addRule( Form::INTEGER, __( 'Provide a full number!', 'packeta' ) )
582 ->addRule( Form::MIN, 'Value must be greater than 0', 1 )
583 ->elseCondition()
584 ->addRule( Form::FLOAT, __( 'Provide a decimal value!', 'packeta' ) )
585 ->addRule( Form::MIN, 'Value must be greater than 0', 0.1 )
586 ->endCondition();
587
588 $container->addText( 'default_height', __( 'Height', 'packeta' ) )
589 ->addConditionOn( $form[ self::FORM_FIELDS_CONTAINER ]['default_dimensions_enabled'], Form::EQUAL, true )
590 ->setRequired()
591 ->addConditionOn( $form[ self::FORM_FIELDS_CONTAINER ]['dimensions_unit'], Form::EQUAL, $this->optionsProvider::DEFAULT_DIMENSIONS_UNIT_MM )
592 ->addRule( Form::INTEGER, __( 'Provide a full number!', 'packeta' ) )
593 ->addRule( Form::MIN, 'Value must be greater than 0', 1 )
594 ->elseCondition()
595 ->addRule( Form::FLOAT, __( 'Provide a decimal value!', 'packeta' ) )
596 ->addRule( Form::MIN, 'Value must be greater than 0', 0.1 )
597 ->endCondition();
598
599 $container->addText( 'default_width', __( 'Width', 'packeta' ) )
600 ->addConditionOn( $form[ self::FORM_FIELDS_CONTAINER ]['default_dimensions_enabled'], Form::EQUAL, true )
601 ->setRequired()
602 ->addConditionOn( $form[ self::FORM_FIELDS_CONTAINER ]['dimensions_unit'], Form::EQUAL, $this->optionsProvider::DEFAULT_DIMENSIONS_UNIT_MM )
603 ->addRule( Form::INTEGER, __( 'Provide a full number!', 'packeta' ) )
604 ->addRule( Form::MIN, 'Value must be greater than 0', 1 )
605 ->elseCondition()
606 ->addRule( Form::FLOAT, __( 'Provide a decimal value!', 'packeta' ) )
607 ->addRule( Form::MIN, 'Value must be greater than 0', 0.1 )
608 ->endCondition();
609
610 $container->addCheckbox( 'replace_shipping_address_with_pickup_point_address', __( 'Replace shipping address with pickup point address', 'packeta' ) )
611 ->setRequired( false );
612
613 $container->addSelect(
614 'checkout_detection',
615 __( 'Force checkout type', 'packeta' ),
616 [
617 OptionsProvider::AUTOMATIC_CHECKOUT_DETECTION => __( 'Automatic detection', 'packeta' ),
618 OptionsProvider::CLASSIC_CHECKOUT_DETECTION => __( 'Classic checkout', 'packeta' ),
619 OptionsProvider::BLOCK_CHECKOUT_DETECTION => __( 'Block-based checkout', 'packeta' ),
620 ]
621 );
622
623 $container->addSelect(
624 'checkout_widget_button_location',
625 __( 'Widget button location in checkout', 'packeta' ),
626 [
627 'after_shipping_rate' => __( 'After shipping rate', 'packeta' ),
628 'after_transport_methods' => __( 'After transport methods', 'packeta' ),
629 ]
630 );
631
632 $container->addCheckbox( 'hide_checkout_logo', __( 'Hide Packeta checkout logo', 'packeta' ) )
633 ->setRequired( false )
634 ->setDefaultValue( OptionsProvider::HIDE_CHECKOUT_LOGO_DEFAULT );
635
636 $container->addSelect(
637 'email_hook',
638 __( 'Hook used to view information in email', 'packeta' ),
639 [
640 'woocommerce_email_footer' => 'woocommerce_email_footer',
641 'woocommerce_email_before_order_table' => 'woocommerce_email_before_order_table',
642 'woocommerce_email_after_order_table' => 'woocommerce_email_after_order_table',
643 'woocommerce_email_order_meta' => 'woocommerce_email_order_meta',
644 ]
645 );
646
647 $container->addCheckbox( 'force_packet_cancel', __( 'Force order cancellation', 'packeta' ) )
648 ->setRequired( false )
649 ->setDefaultValue( OptionsProvider::FORCE_PACKET_CANCEL_DEFAULT );
650
651 $container->addCheckbox( 'widget_auto_open', __( 'Automatically open widget when shipping was selected', 'packeta' ) )
652 ->setRequired( false )
653 ->setDefaultValue( OptionsProvider::WIDGET_AUTO_OPEN_DEFAULT );
654
655 $container->addCheckbox( self::FORM_FIELD_FREE_SHIPPING_SHOWN, __( 'Display the FREE shipping text in checkout', 'packeta' ) )
656 ->setRequired( false )
657 ->setDefaultValue( OptionsProvider::DISPLAY_FREE_SHIPPING_IN_CHECKOUT_DEFAULT );
658
659 $container->addCheckbox( 'prices_include_tax', __( 'Prices include tax', 'packeta' ) )
660 ->setRequired( false )
661 ->setDefaultValue( OptionsProvider::PRICES_INCLUDE_TAX_DEFAULT );
662
663 $form->addSubmit( 'save', __( 'Save changes', 'packeta' ) );
664
665 if ( $this->optionsProvider->has_any( OptionNames::PACKETERY ) ) {
666 $container->setDefaults( $this->optionsProvider->getOptionsByName( OptionNames::PACKETERY ) );
667 }
668
669 return $form;
670 }
671
672 /**
673 * Admin_init callback.
674 */
675 public function admin_init(): void {
676 add_filter( 'pre_update_option_packetery', [ $this, 'validatePacketeryOptions' ] );
677 register_setting( self::FORM_FIELDS_CONTAINER, self::FORM_FIELDS_CONTAINER, [ $this, 'sanitizePacketeryOptions' ] );
678 add_settings_section( 'packetery_main', __( 'Main Settings', 'packeta' ), function () {}, self::SLUG );
679 }
680
681 /**
682 * Validates packetery options.
683 *
684 * @param array $options Options to be validated.
685 *
686 * @return array
687 */
688 public function validatePacketeryOptions( array $options ): array {
689 $form = $this->create_form();
690 /**
691 * Packetery container.
692 *
693 * @var Container $packeteryContainer
694 */
695 $packeteryContainer = $form[ self::FORM_FIELDS_CONTAINER ];
696 $packeteryContainer->setValues( $options );
697 if ( $form->isValid() === false ) {
698 foreach ( $packeteryContainer->getControls() as $control ) {
699 if ( $control->hasErrors() === false ) {
700 continue;
701 }
702
703 add_settings_error( OptionNames::PACKETERY, esc_attr( $control->getName() ), "{$control->getCaption()}: {$control->getError()}" );
704 }
705 }
706
707 $apiPassword = $packeteryContainer['api_password'];
708 if ( $apiPassword instanceof BaseControl && $apiPassword->hasErrors() === false ) {
709 $this->packetaClient->setApiPassword( $apiPassword->getValue() );
710 }
711
712 $this->validateSender( $options['sender'] );
713
714 return $options;
715 }
716
717 /**
718 * Sanitize options.
719 *
720 * @param array $options Options to be sanitized.
721 *
722 * @return array
723 */
724 public function sanitizePacketeryOptions( array $options ): array {
725 $form = $this->create_form();
726 /**
727 * Packetery container.
728 *
729 * @var Container $packeteryContainer
730 */
731 $packeteryContainer = $form[ self::FORM_FIELDS_CONTAINER ];
732 $packeteryContainer->setValues( $options );
733 if ( $form->isValid() === false ) {
734 foreach ( $packeteryContainer->getControls() as $control ) {
735 if ( $control->hasErrors() === false ) {
736 continue;
737 }
738
739 $options[ $control->getName() ] = '';
740 }
741 }
742
743 $apiPassword = $packeteryContainer['api_password'];
744
745 if ( $apiPassword instanceof BaseControl && $apiPassword->hasErrors() === false ) {
746 $apiPass = $apiPassword->getValue();
747 $options['api_key'] = substr( $apiPass, 0, 16 );
748 $this->packetaClient->setApiPassword( $apiPass );
749 } else {
750 $options['api_key'] = '';
751 }
752
753 if ( $packeteryContainer['default_weight'] instanceof BaseControl ) {
754 $defaultWeight = $packeteryContainer['default_weight']->getValue();
755 $options['default_weight'] = is_numeric( $defaultWeight ) ? CoreHelper::trimDecimalPlaces( (float) $defaultWeight, 3 ) : $defaultWeight;
756 }
757
758 foreach ( [ 'default_length', 'default_width', 'default_height' ] as $dimension ) {
759 if ( $packeteryContainer[ $dimension ] instanceof BaseControl ) {
760 $options[ $dimension ] = is_numeric( $packeteryContainer[ $dimension ]->getValue() )
761 ? CoreHelper::trimDecimalPlaces( (float) $packeteryContainer[ $dimension ]->getValue(), 1 )
762 : $packeteryContainer[ $dimension ]->getValue();
763 }
764 }
765
766 if ( $packeteryContainer['force_packet_cancel'] instanceof BaseControl ) {
767 $options['force_packet_cancel'] = (int) $packeteryContainer['force_packet_cancel']->getValue();
768 }
769
770 if ( $packeteryContainer['free_shipping_shown'] instanceof BaseControl ) {
771 $options['free_shipping_shown'] = (int) $packeteryContainer['free_shipping_shown']->getValue();
772 }
773
774 $previousOptions = $this->optionsProvider->getOptionsByName( OptionNames::PACKETERY );
775 if ( ! isset( $options['default_weight_enabled'] ) ) {
776 if ( isset( $previousOptions['default_weight'] ) ) {
777 $options['default_weight'] = $previousOptions['default_weight'];
778 } else {
779 unset( $options['default_weight'] );
780 }
781 }
782 if ( ! isset( $options['default_dimensions_enabled'] ) ) {
783 foreach ( [ 'default_length', 'default_width', 'default_height' ] as $dimension ) {
784 if ( isset( $previousOptions[ $dimension ] ) ) {
785 $options[ $dimension ] = $previousOptions[ $dimension ];
786 } else {
787 unset( $options[ $dimension ] );
788 }
789 }
790 }
791
792 return $options;
793 }
794
795 /**
796 * Validates sender.
797 *
798 * @param string $senderLabel Sender label.
799 *
800 * @return bool|null
801 */
802 private function validateSender( string $senderLabel ): ?bool {
803 $senderValidationRequest = new SenderGetReturnRouting( $senderLabel );
804 $senderValidationResponse = $this->packetaClient->senderGetReturnRouting( $senderValidationRequest );
805
806 $senderValidationLog = new Log\Record();
807 $senderValidationLog->action = Log\Record::ACTION_SENDER_VALIDATION;
808
809 $senderValidationLog->status = Log\Record::STATUS_SUCCESS;
810 $senderValidationLog->title = __( 'Sender validation was successful', 'packeta' );
811
812 if ( $senderValidationResponse->hasFault() ) {
813 $senderValidationLog->status = Log\Record::STATUS_ERROR;
814 $senderValidationLog->params = [
815 'errorMessage' => $senderValidationResponse->getFaultString(),
816 ];
817 $senderValidationLog->title = __( 'Sender could not be validated', 'packeta' );
818 }
819
820 $this->logger->add( $senderValidationLog );
821
822 $senderExists = $senderValidationResponse->senderExists();
823
824 if ( $senderExists === false ) {
825 $this->messageManager->flash_message( __( 'Specified sender does not exist', 'packeta' ), MessageManager::TYPE_INFO, MessageManager::RENDERER_PACKETERY, 'plugin-options' );
826 }
827
828 if ( $senderExists === null ) {
829 $this->messageManager->flash_message( __( 'Unable to check specified sender', 'packeta' ), MessageManager::TYPE_INFO, MessageManager::RENDERER_PACKETERY, 'plugin-options' );
830 }
831
832 return $senderExists;
833 }
834
835 /**
836 * Process actions.
837 *
838 * @return void
839 */
840 public function processActions(): void {
841 $action = $this->httpRequest->getQuery( 'action' );
842 if ( $action === self::ACTION_VALIDATE_SENDER ) {
843 $result = $this->validateSender( $this->optionsProvider->get_sender() );
844
845 if ( $result === true ) {
846 $this->messageManager->flash_message( __( 'Specified sender has been validated.', 'packeta' ), MessageManager::TYPE_SUCCESS, MessageManager::RENDERER_PACKETERY, 'plugin-options' );
847 }
848
849 $doRedirect = wp_safe_redirect(
850 add_query_arg(
851 [
852 'page' => self::SLUG,
853 ],
854 get_admin_url( null, 'admin.php' )
855 )
856 );
857
858 if ( $doRedirect ) {
859 exit;
860 }
861 }
862
863 $packetStatusSyncForm = $this->createPacketStatusSyncForm();
864 if (
865 $packetStatusSyncForm['save'] instanceof SubmitButton &&
866 $packetStatusSyncForm['save']->isSubmittedBy()
867 ) {
868 $packetStatusSyncForm->fireEvents();
869 }
870
871 $autoSubmissionForm = $this->createAutoSubmissionForm();
872 if (
873 $autoSubmissionForm['save'] instanceof SubmitButton &&
874 $autoSubmissionForm['save']->isSubmittedBy()
875 ) {
876 $autoSubmissionForm->fireEvents();
877 }
878
879 $advancedForm = $this->createAdvancedForm();
880 if (
881 $advancedForm['save'] instanceof SubmitButton &&
882 $advancedForm['save']->isSubmittedBy()
883 ) {
884 $advancedForm->fireEvents();
885 }
886 }
887
888 /**
889 * Renders page.
890 */
891 public function render(): void {
892 $activeTab = ( $this->httpRequest->getQuery( self::PARAM_TAB ) ?? self::TAB_GENERAL );
893
894 $latteParams = [];
895 if ( $activeTab === self::TAB_PACKET_STATUS_SYNC ) {
896 $latteParams = [ 'form' => $this->createPacketStatusSyncForm() ];
897 } elseif ( $activeTab === self::TAB_AUTO_SUBMISSION ) {
898 $latteParams = [ 'form' => $this->createAutoSubmissionForm() ];
899 } elseif ( $activeTab === self::TAB_ADVANCED ) {
900 $latteParams = [ 'form' => $this->createAdvancedForm() ];
901 } elseif ( $activeTab === self::TAB_GENERAL ) {
902 $latteParams = [ 'form' => $this->create_form() ];
903 }
904
905 if ( ! extension_loaded( 'soap' ) ) {
906 $latteParams['error'] = __( 'This plugin requires an active SOAP library for proper operation. Contact your web hosting administrator.', 'packeta' );
907 }
908
909 $latteParams['apiPasswordLink'] = trim( $this->latteEngine->renderToString( PACKETERY_PLUGIN_DIR . '/template/options/help-block-link.latte', [ 'href' => 'https://client.packeta.com/support' ] ) );
910
911 $latteParams['exportLink'] = add_query_arg(
912 [
913 'page' => self::SLUG,
914 'action' => Exporter::ACTION_EXPORT_SETTINGS,
915 ],
916 get_admin_url( null, 'admin.php' )
917 );
918
919 $latteParams['activeTab'] = ( $this->httpRequest->getQuery( self::PARAM_TAB ) ?? self::TAB_GENERAL );
920 $latteParams['generalTabLink'] = $this->createLink();
921 $latteParams['advancedTabLink'] = $this->createLink( self::TAB_ADVANCED );
922 $latteParams['supportTabLink'] = $this->createLink( self::TAB_SUPPORT );
923 $latteParams['packetStatusSyncTabLink'] = $this->createLink( self::TAB_PACKET_STATUS_SYNC );
924 $latteParams['autoSubmissionTabLink'] = $this->createLink( self::TAB_AUTO_SUBMISSION );
925
926 $latteParams['canValidateSender'] = (bool) $this->optionsProvider->get_sender();
927 $latteParams['senderValidationLink'] = add_query_arg(
928 [
929 'page' => self::SLUG,
930 'action' => self::ACTION_VALIDATE_SENDER,
931 ],
932 get_admin_url( null, 'admin.php' )
933 );
934
935 $lastExport = null;
936 $lastExportOption = get_option( OptionNames::LAST_SETTINGS_EXPORT );
937 if ( $lastExportOption !== false ) {
938 $date = DateTime::createFromFormat( DATE_ATOM, $lastExportOption );
939 if ( $date !== false ) {
940 $date->setTimezone( wp_timezone() );
941 $lastExport = $date->format( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) );
942 }
943 }
944 if ( $lastExport !== null ) {
945 $latteParams['lastExport'] = $lastExport;
946 }
947
948 $latteParams['forcePacketCancelDescription'] = __( 'Cancel the packet for an order even if the cancellation in the Packeta system will not be successful.', 'packeta' );
949 $latteParams['messages'] = $this->messageManager->renderToString( MessageManager::RENDERER_PACKETERY, 'plugin-options' );
950 $latteParams['isCzechLocale'] = $this->moduleHelper->isCzechLocale();
951 $latteParams['logoZasilkovna'] = $this->urlBuilder->buildAssetUrl( 'public/images/logo-zasilkovna.svg' );
952 $latteParams['logoPacketa'] = $this->urlBuilder->buildAssetUrl( 'public/images/logo-packeta.svg' );
953 $advancedCarrierSettingsDescription = sprintf(
954 // translators: first %s is line break, second one is e-mail address
955 __( 'BETA: Once enabled, Packeta carriers will appear as separate shipping methods in WooCommerce - Settings - Shipping. After enabling this feature, you will need to set up shipping methods in WooCommerce again.%1$sThis is an experimental feature. If you experience any issues, please email us at %2$s with a description of the issue.', 'packeta' ),
956 '<br>',
957 '<a href="mailto:' . $this->supportEmailAddress . '">' . $this->supportEmailAddress . '</a>'
958 );
959 $latteParams['translations'] = [
960 'packeta' => __( 'Packeta', 'packeta' ),
961 'title' => __( 'Options', 'packeta' ),
962 'general' => __( 'General', 'packeta' ),
963 'packetAutoSubmission' => __( 'Packet auto-submission', 'packeta' ),
964 'packetAutoSubmissionMappingDescription' => __( 'Choose events for payment methods that will trigger packet submission', 'packeta' ),
965 // translators: %s represents URL.
966 'apiPasswordCanBeFoundAt%sUrl' => __( 'API password can be found at %s', 'packeta' ),
967
968 'saveChanges' => __( 'Save changes', 'packeta' ),
969 'validateSender' => __( 'Validate sender', 'packeta' ),
970 'advanced' => __( 'Advanced', 'packeta' ),
971 'support' => __( 'Support', 'packeta' ),
972 'optionsExportInfo1' => __(
973 'By clicking the button, you will export the settings of your plugin into a separate file. The export does not contain any sensitive information about your e-shop. Please send the resulting file to the technical support of Packeta (you can find the e-mail address here:',
974 'packeta'
975 ),
976 'optionsExportInfo2' => __(
977 ') along with the description of your problem. For a better understanding of your problem, we recommend adding screenshots, which show the problem (if possible).',
978 'packeta'
979 ),
980 'exportPluginSettings' => __( 'Export the plugin settings', 'packeta' ),
981 'settingsExportDatetime' => __( 'Date and time of the last export of settings', 'packeta' ),
982 'settingsNotYetExported' => __( 'The settings have not been exported yet.', 'packeta' ),
983 'senderDescription' => sprintf(
984 /* translators: 1: emphasis start 2: emphasis end 3: client section link start 4: client section link end */
985 esc_html__( 'Fill here %1$ssender label%2$s - you will find it in %3$sclient section%4$s - user information - field \'Indication\'.', 'packeta' ),
986 '<strong>',
987 '</strong>',
988 '<a href="https://client.packeta.com/senders" target="_blank">',
989 '</a>'
990 ),
991 'advancedCarrierSettingsDescription' => $advancedCarrierSettingsDescription,
992 'packagingWeightDescription' => __( 'This parameter is used to determine the weight of the packaging material. This value is automatically added to the total weight of each order that contains products with non-zero weight. This value is also taken into account when evaluating the weight rules in the cart.', 'packeta' ),
993 'defaultWeightDescription' => __( 'This value is automatically added to the total weight of each order that contains products with zero weight.', 'packeta' ),
994 'defaultDimensionsDescription' => __( 'These dimensions will be applied to the packet by default, if required by the carrier.', 'packeta' ),
995 'setCheckoutDetectionDescription' => __( 'If you have trouble displaying the widget button in the checkout, you can force what type of checkout you are using.', 'packeta' ),
996 'packetStatusSyncTabLinkLabel' => __( 'Packet status tracking', 'packeta' ),
997 'statusSyncingOrderStatusesLabel' => __( 'Order statuses, for which cron will check the packet status', 'packeta' ),
998 'statusSyncingOrderStatusesDescription' => __( 'Cron will automatically track all orders with these statuses and check if the shipment status has changed.', 'packeta' ),
999 'statusSyncingPacketStatusesLabel' => __( 'Packet statuses that are being checked', 'packeta' ),
1000 'statusSyncingPacketStatusesDescription' => __( 'If an order has a shipment with one of these selected statuses, the shipment status will be tracked.', 'packeta' ),
1001 'numberOfDaysToCheckDescription' => __( 'Number of days after the creation of an order, during which the order status will be checked.', 'packeta' ),
1002 'widgetAutoOpenDescription' => __( 'If this option is active, the widget for selecting pickup points will open automatically after selecting the shipping method at the checkout.', 'packeta' ),
1003 'autoOrderStatusChangeDescription' => __( 'Change order status after data submission to Packeta.', 'packeta' ),
1004 'freeShippingTextDescription' => __( 'If enabled, "FREE" will be displayed after the name of the shipping method, if free shipping is applied.', 'packeta' ),
1005 'orderStatusChangeSettings' => __( 'Order status change settings', 'packeta' ),
1006 'dimensionsLabel' => __( 'Dimensions', 'packeta' ),
1007 ];
1008
1009 $this->latteEngine->render( PACKETERY_PLUGIN_DIR . '/template/options/page.latte', $latteParams );
1010 }
1011
1012 /**
1013 * Creates tab link.
1014 *
1015 * @param string|null $tab Tab ID.
1016 * @return string
1017 */
1018 public function createLink( ?string $tab = null ): string {
1019 $params = [
1020 'page' => self::SLUG,
1021 ];
1022
1023 if ( $tab !== null ) {
1024 $params[ self::PARAM_TAB ] = $tab;
1025 }
1026
1027 return add_query_arg(
1028 $params,
1029 get_admin_url( null, 'admin.php' )
1030 );
1031 }
1032 }
1033