PluginProbe
Packeta / 1.4.3
Packeta v1.4.3
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 / Carrier / OptionsPage.php

OptionsPage.php in Packeta 1.4.3, at src/Packetery/Module/Carrier/OptionsPage.php

476 lines 14.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class OptionsPage
4 *
5 * @package Packetery\Options
6 */
7
8 declare( strict_types=1 );
9
10 namespace Packetery\Module\Carrier;
11
12 use Packetery\Core\Helper;
13 use Packetery\Module\Checkout;
14 use Packetery\Module\FormFactory;
15 use Packetery\Module\FormValidators;
16 use Packetery\Module\MessageManager;
17 use PacketeryLatte\Engine;
18 use PacketeryNette\Forms\Container;
19 use PacketeryNette\Forms\Form;
20 use PacketeryNette\Http\Request;
21
22 /**
23 * Class OptionsPage
24 *
25 * @package Packetery\Options
26 */
27 class OptionsPage {
28
29 public const FORM_FIELD_NAME = 'name';
30 public const SLUG = 'packeta-country';
31
32 /**
33 * PacketeryLatte_engine.
34 *
35 * @var Engine PacketeryLatte engine.
36 */
37 private $latteEngine;
38
39 /**
40 * Carrier repository.
41 *
42 * @var Repository Carrier repository.
43 */
44 private $carrierRepository;
45
46 /**
47 * Form factory.
48 *
49 * @var FormFactory Form factory.
50 */
51 private $formFactory;
52
53 /**
54 * PacketeryNette Request.
55 *
56 * @var Request PacketeryNette Request.
57 */
58 private $httpRequest;
59
60 /**
61 * CountryListingPage.
62 *
63 * @var CountryListingPage CountryListingPage.
64 */
65 private $countryListingPage;
66
67 /**
68 * Message manager.
69 *
70 * @var MessageManager
71 */
72 private $messageManager;
73
74 /**
75 * Plugin constructor.
76 *
77 * @param Engine $latteEngine PacketeryLatte_engine.
78 * @param Repository $carrierRepository Carrier repository.
79 * @param FormFactory $formFactory Form factory.
80 * @param Request $httpRequest PacketeryNette Request.
81 * @param CountryListingPage $countryListingPage CountryListingPage.
82 * @param MessageManager $messageManager Message manager.
83 */
84 public function __construct(
85 Engine $latteEngine,
86 Repository $carrierRepository,
87 FormFactory $formFactory,
88 Request $httpRequest,
89 CountryListingPage $countryListingPage,
90 MessageManager $messageManager
91 ) {
92 $this->latteEngine = $latteEngine;
93 $this->carrierRepository = $carrierRepository;
94 $this->formFactory = $formFactory;
95 $this->httpRequest = $httpRequest;
96 $this->countryListingPage = $countryListingPage;
97 $this->messageManager = $messageManager;
98 }
99
100 /**
101 * Registers WP callbacks.
102 */
103 public function register(): void {
104 add_submenu_page(
105 \Packetery\Module\Options\Page::SLUG,
106 __( 'Carrier settings', 'packeta' ),
107 __( 'Carrier settings', 'packeta' ),
108 'manage_options',
109 self::SLUG,
110 array(
111 $this,
112 'render',
113 ),
114 10
115 );
116 }
117
118 /**
119 * Creates settings form.
120 *
121 * @param array $carrierData Carrier data.
122 *
123 * @return Form
124 */
125 private function createForm( array $carrierData ): Form {
126 $optionId = Checkout::CARRIER_PREFIX . $carrierData['id'];
127
128 $form = $this->formFactory->create( $optionId );
129
130 $form->addCheckbox(
131 'active',
132 __( 'Active carrier', 'packeta' ) . ':'
133 );
134
135 $form->addText( self::FORM_FIELD_NAME, __( 'Display name', 'packeta' ) . ':' )
136 ->setRequired();
137
138 $weightLimits = $form->addContainer( 'weight_limits' );
139 if ( empty( $carrierData['weight_limits'] ) ) {
140 $this->addWeightLimit( $weightLimits, 0 );
141 } else {
142 foreach ( $carrierData['weight_limits'] as $index => $limit ) {
143 $this->addWeightLimit( $weightLimits, $index );
144 }
145 }
146
147 $form->addText( 'default_COD_surcharge', __( 'Default COD surcharge', 'packeta' ) . ':' )
148 ->setRequired( false )
149 ->addRule( Form::FLOAT )
150 ->addRule( Form::MIN, null, 0 );
151
152 $surchargeLimits = $form->addContainer( 'surcharge_limits' );
153 if ( ! empty( $carrierData['surcharge_limits'] ) ) {
154 foreach ( $carrierData['surcharge_limits'] as $index => $limit ) {
155 $this->addSurchargeLimit( $surchargeLimits, $index );
156 }
157 }
158
159 $item = $form->addText( 'free_shipping_limit', __( 'Free shipping limit', 'packeta' ) . ':' );
160 $item->addRule( $form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
161 $form->addHidden( 'id' )->setRequired();
162 $form->addSubmit( 'save' );
163
164 $carrier = $this->carrierRepository->getAnyById( (string) $carrierData['id'] );
165 if ( false === $carrier->hasPickupPoints() ) {
166 $addressValidationOptions = [
167 'none' => __( 'No address validation', 'packeta' ),
168 'optional' => __( 'Optional address validation', 'packeta' ),
169 'required' => __( 'Required address validation', 'packeta' ),
170 ];
171 $form->addSelect( 'address_validation', __( 'Address validation', 'packeta' ) . ':', $addressValidationOptions )
172 ->setDefaultValue( 'none' );
173 }
174
175 if ( $carrier->supportsAgeVerification() ) {
176 $form->addText( 'age_verification_fee', __( 'Age verification fee', 'packeta' ) . ':' )
177 ->setRequired( false )
178 ->addRule( Form::FLOAT )
179 ->addRule( Form::MIN, null, 0 );
180 }
181
182 $form->onValidate[] = [ $this, 'validateOptions' ];
183 $form->onSuccess[] = [ $this, 'updateOptions' ];
184
185 $carrierOptions = get_option( $optionId );
186 $carrierOptions['id'] = $carrierData['id'];
187 if ( empty( $carrierOptions[ self::FORM_FIELD_NAME ] ) ) {
188 $carrierOptions[ self::FORM_FIELD_NAME ] = $carrierData['name'];
189 }
190 $form->setDefaults( $carrierOptions );
191
192 return $form;
193 }
194
195 /**
196 * Creates settings form.
197 *
198 * @param array $carrierData Carrier data.
199 *
200 * @return Form
201 */
202 private function createFormTemplate( array $carrierData ): Form {
203 $optionId = Checkout::CARRIER_PREFIX . $carrierData['id'];
204
205 $form = $this->formFactory->create( $optionId . '_template' );
206
207 $weightLimitsTemplate = $form->addContainer( 'weight_limits' );
208 $this->addWeightLimit( $weightLimitsTemplate, 0 );
209
210 $surchargeLimitsTemplate = $form->addContainer( 'surcharge_limits' );
211 $this->addSurchargeLimit( $surchargeLimitsTemplate, 0 );
212
213 return $form;
214 }
215
216 /**
217 * Validates options.
218 *
219 * @param Form $form Form.
220 */
221 public function validateOptions( Form $form ): void {
222 if ( $form->hasErrors() ) {
223 add_settings_error( '', '', esc_attr( __( 'Some carrier data is invalid', 'packeta' ) ) );
224 return;
225 }
226
227 $options = $form->getValues( 'array' );
228
229 $this->checkOverlapping(
230 $form,
231 $options,
232 'weight_limits',
233 'weight',
234 __( 'Weight rules are overlapping, please fix them.', 'packeta' )
235 );
236 $this->checkOverlapping(
237 $form,
238 $options,
239 'surcharge_limits',
240 'order_price',
241 __( 'Surcharge rules are overlapping, please fix them.', 'packeta' )
242 );
243 }
244
245 /**
246 * Saves carrier options. onSuccess callback.
247 *
248 * @param Form $form Form.
249 *
250 * @return void
251 */
252 public function updateOptions( Form $form ): void {
253 $options = $form->getValues( 'array' );
254
255 $options = $this->mergeNewLimits( $options, 'weight_limits' );
256 $options = $this->sortLimits( $options, 'weight_limits', 'weight' );
257 $options = $this->mergeNewLimits( $options, 'surcharge_limits' );
258 $options = $this->sortLimits( $options, 'surcharge_limits', 'order_price' );
259
260 update_option( Checkout::CARRIER_PREFIX . $options['id'], $options );
261 $this->messageManager->flash_message( __( 'Settings saved', 'packeta' ), MessageManager::TYPE_SUCCESS, MessageManager::RENDERER_PACKETERY, 'carrier-country' );
262
263 if ( wp_safe_redirect(
264 add_query_arg(
265 [
266 'page' => self::SLUG,
267 'code' => $this->httpRequest->getQuery( 'code' ),
268 ],
269 get_admin_url( null, 'admin.php' )
270 ),
271 303
272 ) ) {
273 exit;
274 }
275 }
276
277 /**
278 * Renders page.
279 */
280 public function render(): void {
281 $countryIso = $this->httpRequest->getQuery( 'code' );
282 if ( $countryIso ) {
283 $countryCarriers = $this->carrierRepository->getByCountryIncludingZpoints( $countryIso );
284 $carriersData = [];
285 $post = $this->httpRequest->getPost();
286 foreach ( $countryCarriers as $carrier ) {
287 if ( ! empty( $post ) && $post['id'] === $carrier->getId() ) {
288 $formTemplate = $this->createFormTemplate( $post );
289 $form = $this->createForm( $post );
290 if ( $form->isSubmitted() ) {
291 $form->fireEvents();
292 }
293 } else {
294 $carrierData = $carrier->__toArray();
295 $options = get_option( Checkout::CARRIER_PREFIX . $carrier->getId() );
296 if ( false !== $options ) {
297 $carrierData += $options;
298 }
299 $formTemplate = $this->createFormTemplate( $carrierData );
300 $form = $this->createForm( $carrierData );
301 }
302
303 $carriersData[] = [
304 'form' => $form,
305 'formTemplate' => $formTemplate,
306 'carrier' => $carrier,
307 ];
308 }
309
310 $this->latteEngine->render(
311 PACKETERY_PLUGIN_DIR . '/template/carrier/country.latte',
312 [
313 'forms' => $carriersData,
314 'country_iso' => $countryIso,
315 'globalCurrency' => get_woocommerce_currency_symbol(),
316 'flashMessages' => $this->messageManager->renderToString( MessageManager::RENDERER_PACKETERY, 'carrier-country' ),
317 'translations' => [
318 'cannotUseThisCarrierBecauseRequiresCustomsDeclaration' => __( 'This carrier cannot be used, because it requires a customs declaration.', 'packeta' ),
319 'delete' => __( 'Delete', 'packeta' ),
320 'weightRules' => __( 'Weight rules', 'packeta' ),
321 'addWeightRule' => __( 'Add weight rule', 'packeta' ),
322 'codSurchargeRules' => __( 'COD surcharge rules', 'packeta' ),
323 'addCodSurchargeRule' => __( 'Add COD surcharge rule', 'packeta' ),
324 'afterExceedingThisAmountShippingIsFree' => __( 'After exceeding this amount, shipping is free.', 'packeta' ),
325 'addressValidationDescription' => __( 'Customer address validation.', 'packeta' ),
326 'saveChanges' => __( 'Save changes', 'packeta' ),
327 'packeta' => __( 'Packeta', 'packeta' ),
328 'countryOptions' => __( 'Country options', 'packeta' ),
329 'noKnownCarrierForThisCountry' => __( 'No carriers available for this country.', 'packeta' ),
330 'ageVerificationSupportedNotification' => __( 'When shipping via this carrier, you can order the Age Verification service. The service will get ordered automatically if there is at least 1 product in the order with the age verification setting.', 'packeta' ),
331 ],
332 ]
333 );
334 } else {
335 $this->countryListingPage->render();
336 }
337 }
338
339 /**
340 * Transforms new_ keys to common numeric.
341 *
342 * @param array $options Options to merge.
343 * @param string $limitsContainer Container id.
344 *
345 * @return array
346 */
347 private function mergeNewLimits( array $options, string $limitsContainer ): array {
348 $newOptions = [];
349 if ( isset( $options[ $limitsContainer ] ) ) {
350 foreach ( $options[ $limitsContainer ] as $key => $option ) {
351 if ( is_int( $key ) ) {
352 $newOptions[ $key ] = $option;
353 }
354 if ( 0 === strpos( (string) $key, 'new_' ) ) {
355 $newOptions[] = $option;
356 }
357 }
358 $options[ $limitsContainer ] = $newOptions;
359 }
360
361 return $options;
362 }
363
364 /**
365 * Checks rules overlapping.
366 *
367 * @param Form $form Form.
368 * @param array $options Form data.
369 * @param string $limitsContainer Container id.
370 * @param string $limitKey Rule id.
371 * @param string $overlappingMessage Error message.
372 *
373 * @return void
374 */
375 private function checkOverlapping( Form $form, array $options, string $limitsContainer, string $limitKey, string $overlappingMessage ): void {
376 $limits = array_column( $options[ $limitsContainer ], $limitKey );
377 if ( count( array_unique( $limits, SORT_NUMERIC ) ) !== count( $limits ) ) {
378 add_settings_error( $limitsContainer, $limitsContainer, esc_attr( $overlappingMessage ) );
379 $form->addError( $overlappingMessage );
380 }
381 }
382
383 /**
384 * Sorts rules.
385 *
386 * @param array $options Form data.
387 * @param string $limitsContainer Container id.
388 * @param string $limitKey Rule id.
389 *
390 * @return array
391 */
392 private function sortLimits( array $options, string $limitsContainer, string $limitKey ): array {
393 $limits = array_column( $options[ $limitsContainer ], $limitKey );
394 array_multisort( $limits, SORT_ASC, $options[ $limitsContainer ] );
395
396 return $options;
397 }
398
399 /**
400 * Adds limit fields to form.
401 *
402 * @param Container $weightLimits Container.
403 * @param int|string $index Index.
404 *
405 * @return void
406 */
407 private function addWeightLimit( Container $weightLimits, $index ): void {
408 $limit = $weightLimits->addContainer( (string) $index );
409 $item = $limit->addText( 'weight', __( 'Weight up to', 'packeta' ) . ':' );
410 $item->setRequired();
411 $item->addRule( Form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
412 // translators: %d is numeric threshold.
413 $item->addRule( [ FormValidators::class, 'greaterThan' ], __( 'Enter number greater than %d', 'packeta' ), 0.0 );
414
415 $item->addFilter(
416 function ( float $value ) {
417 return Helper::simplifyWeight( $value );
418 }
419 );
420 // translators: %d is numeric threshold.
421 $item->addRule( [ FormValidators::class, 'greaterThan' ], __( 'Enter number greater than %d', 'packeta' ), 0.0 );
422
423 $item = $limit->addText( 'price', __( 'Price', 'packeta' ) . ':' );
424 $item->setRequired();
425 $item->addRule( Form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
426 $item->addRule( Form::MIN, null, 0 );
427 }
428
429 /**
430 * Adds limit fields to form.
431 *
432 * @param Container $surchargeLimits Container.
433 * @param int|string $index Index.
434 *
435 * @return void
436 */
437 private function addSurchargeLimit( Container $surchargeLimits, $index ): void {
438 $limit = $surchargeLimits->addContainer( (string) $index );
439 $item = $limit->addText( 'order_price', __( 'Order price up to', 'packeta' ) . ':' );
440 $item->setRequired();
441 $item->addRule( Form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
442 $item->addRule( Form::MIN, null, 0 );
443 $item->addCondition( Form::MAX, 0 )
444 ->addCondition( Form::MIN, 0 )
445 // translators: %d is the value.
446 ->addRule( Form::BLANK, __( 'Value must not be %d', 'packeta' ), 0 );
447
448 $item = $limit->addText( 'surcharge', __( 'Surcharge', 'packeta' ) . ':' );
449 $item->setRequired();
450 $item->addRule( Form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
451 $item->addRule( Form::MIN, null, 0 );
452 }
453
454 /**
455 * Creates link to page.
456 *
457 * @param string|null $countryCode Country code.
458 *
459 * @return string
460 */
461 public function createUrl( ?string $countryCode = null ): string {
462 $params = [
463 'page' => self::SLUG,
464 ];
465
466 if ( null !== $countryCode ) {
467 $params['code'] = $countryCode;
468 }
469
470 return add_query_arg(
471 $params,
472 admin_url( 'admin.php' )
473 );
474 }
475 }
476