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

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

447 lines 13.1 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->getById( (int) $carrierData['id'] );
165 if ( $carrier && 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 $form->onValidate[] = [ $this, 'validateOptions' ];
176 $form->onSuccess[] = [ $this, 'updateOptions' ];
177
178 $carrierOptions = get_option( $optionId );
179 $carrierOptions['id'] = $carrierData['id'];
180 if ( empty( $carrierOptions[ self::FORM_FIELD_NAME ] ) ) {
181 $carrierOptions[ self::FORM_FIELD_NAME ] = $carrierData['name'];
182 }
183 $form->setDefaults( $carrierOptions );
184
185 return $form;
186 }
187
188 /**
189 * Creates settings form.
190 *
191 * @param array $carrierData Carrier data.
192 *
193 * @return Form
194 */
195 private function createFormTemplate( array $carrierData ): Form {
196 $optionId = Checkout::CARRIER_PREFIX . $carrierData['id'];
197
198 $form = $this->formFactory->create( $optionId . '_template' );
199
200 $weightLimitsTemplate = $form->addContainer( 'weight_limits' );
201 $this->addWeightLimit( $weightLimitsTemplate, 0 );
202
203 $surchargeLimitsTemplate = $form->addContainer( 'surcharge_limits' );
204 $this->addSurchargeLimit( $surchargeLimitsTemplate, 0 );
205
206 return $form;
207 }
208
209 /**
210 * Validates options.
211 *
212 * @param Form $form Form.
213 */
214 public function validateOptions( Form $form ): void {
215 if ( $form->hasErrors() ) {
216 add_settings_error( '', '', esc_attr( __( 'Some carrier data is invalid', 'packeta' ) ) );
217 return;
218 }
219
220 $options = $form->getValues( 'array' );
221
222 $this->checkOverlapping(
223 $form,
224 $options,
225 'weight_limits',
226 'weight',
227 __( 'Weight rules are overlapping, please fix them.', 'packeta' )
228 );
229 $this->checkOverlapping(
230 $form,
231 $options,
232 'surcharge_limits',
233 'order_price',
234 __( 'Surcharge rules are overlapping, please fix them.', 'packeta' )
235 );
236 }
237
238 /**
239 * Saves carrier options. onSuccess callback.
240 *
241 * @param Form $form Form.
242 *
243 * @return void
244 */
245 public function updateOptions( Form $form ): void {
246 $options = $form->getValues( 'array' );
247
248 $options = $this->mergeNewLimits( $options, 'weight_limits' );
249 $options = $this->sortLimits( $options, 'weight_limits', 'weight' );
250 $options = $this->mergeNewLimits( $options, 'surcharge_limits' );
251 $options = $this->sortLimits( $options, 'surcharge_limits', 'order_price' );
252
253 update_option( Checkout::CARRIER_PREFIX . $options['id'], $options );
254 $this->messageManager->flash_message( __( 'Settings saved', 'packeta' ), MessageManager::TYPE_SUCCESS, MessageManager::RENDERER_PACKETERY, 'carrier-country' );
255
256 if ( wp_safe_redirect(
257 add_query_arg(
258 [
259 'page' => self::SLUG,
260 'code' => $this->httpRequest->getQuery( 'code' ),
261 ],
262 get_admin_url( null, 'admin.php' )
263 ),
264 303
265 ) ) {
266 exit;
267 }
268 }
269
270 /**
271 * Renders page.
272 */
273 public function render(): void {
274 $countryIso = $this->httpRequest->getQuery( 'code' );
275 if ( $countryIso ) {
276 $countryCarriers = $this->carrierRepository->getByCountryIncludingZpoints( $countryIso );
277 $carriersData = [];
278 $post = $this->httpRequest->getPost();
279 foreach ( $countryCarriers as $carrier ) {
280 if ( ! empty( $post ) && $post['id'] === $carrier->getId() ) {
281 $formTemplate = $this->createFormTemplate( $post );
282 $form = $this->createForm( $post );
283 if ( $form->isSubmitted() ) {
284 $form->fireEvents();
285 }
286 } else {
287 $carrierData = $carrier->__toArray();
288 $options = get_option( Checkout::CARRIER_PREFIX . $carrier->getId() );
289 if ( false !== $options ) {
290 $carrierData += $options;
291 }
292 $formTemplate = $this->createFormTemplate( $carrierData );
293 $form = $this->createForm( $carrierData );
294 }
295
296 $carriersData[] = [
297 'form' => $form,
298 'formTemplate' => $formTemplate,
299 'carrier' => $carrier,
300 ];
301 }
302
303 $this->latteEngine->render(
304 PACKETERY_PLUGIN_DIR . '/template/carrier/country.latte',
305 [
306 'forms' => $carriersData,
307 'country_iso' => $countryIso,
308 'globalCurrency' => get_woocommerce_currency_symbol(),
309 'flashMessages' => $this->messageManager->renderToString( MessageManager::RENDERER_PACKETERY, 'carrier-country' ),
310 'translations' => [
311 'cannotUseThisCarrierBecauseRequiresCustomsDeclaration' => __( 'This carrier cannot be used, because it requires a customs declaration.', 'packeta' ),
312 'delete' => __( 'Delete', 'packeta' ),
313 'weightRules' => __( 'Weight rules', 'packeta' ),
314 'addWeightRule' => __( 'Add weight rule', 'packeta' ),
315 'codSurchargeRules' => __( 'COD surcharge rules', 'packeta' ),
316 'addCodSurchargeRule' => __( 'Add COD surcharge rule', 'packeta' ),
317 'afterExceedingThisAmountShippingIsFree' => __( 'After exceeding this amount, shipping is free.', 'packeta' ),
318 'addressValidationDescription' => __( 'Customer address validation.', 'packeta' ),
319 'saveChanges' => __( 'Save changes', 'packeta' ),
320 'packeta' => __( 'Packeta', 'packeta' ),
321 'countryOptions' => __( 'Country options', 'packeta' ),
322 'noKnownCarrierForThisCountry' => __( 'No carriers available for this country.', 'packeta' ),
323 ],
324 ]
325 );
326 } else {
327 $this->countryListingPage->render();
328 }
329 }
330
331 /**
332 * Transforms new_ keys to common numeric.
333 *
334 * @param array $options Options to merge.
335 * @param string $limitsContainer Container id.
336 *
337 * @return array
338 */
339 private function mergeNewLimits( array $options, string $limitsContainer ): array {
340 $newOptions = [];
341 if ( isset( $options[ $limitsContainer ] ) ) {
342 foreach ( $options[ $limitsContainer ] as $key => $option ) {
343 if ( is_int( $key ) ) {
344 $newOptions[ $key ] = $option;
345 }
346 if ( 0 === strpos( (string) $key, 'new_' ) ) {
347 $newOptions[] = $option;
348 }
349 }
350 $options[ $limitsContainer ] = $newOptions;
351 }
352
353 return $options;
354 }
355
356 /**
357 * Checks rules overlapping.
358 *
359 * @param Form $form Form.
360 * @param array $options Form data.
361 * @param string $limitsContainer Container id.
362 * @param string $limitKey Rule id.
363 * @param string $overlappingMessage Error message.
364 *
365 * @return void
366 */
367 private function checkOverlapping( Form $form, array $options, string $limitsContainer, string $limitKey, string $overlappingMessage ): void {
368 $limits = array_column( $options[ $limitsContainer ], $limitKey );
369 if ( count( array_unique( $limits, SORT_NUMERIC ) ) !== count( $limits ) ) {
370 add_settings_error( $limitsContainer, $limitsContainer, esc_attr( $overlappingMessage ) );
371 $form->addError( $overlappingMessage );
372 }
373 }
374
375 /**
376 * Sorts rules.
377 *
378 * @param array $options Form data.
379 * @param string $limitsContainer Container id.
380 * @param string $limitKey Rule id.
381 *
382 * @return array
383 */
384 private function sortLimits( array $options, string $limitsContainer, string $limitKey ): array {
385 $limits = array_column( $options[ $limitsContainer ], $limitKey );
386 array_multisort( $limits, SORT_ASC, $options[ $limitsContainer ] );
387
388 return $options;
389 }
390
391 /**
392 * Adds limit fields to form.
393 *
394 * @param Container $weightLimits Container.
395 * @param int|string $index Index.
396 *
397 * @return void
398 */
399 private function addWeightLimit( Container $weightLimits, $index ): void {
400 $limit = $weightLimits->addContainer( (string) $index );
401 $item = $limit->addText( 'weight', __( 'Weight up to', 'packeta' ) . ':' );
402 $item->setRequired();
403 $item->addRule( Form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
404 // translators: %d is numeric threshold.
405 $item->addRule( [ FormValidators::class, 'greaterThan' ], __( 'Enter number greater than %d', 'packeta' ), 0.0 );
406
407 $item->addFilter(
408 function ( float $value ) {
409 return Helper::simplifyWeight( $value );
410 }
411 );
412 // translators: %d is numeric threshold.
413 $item->addRule( [ FormValidators::class, 'greaterThan' ], __( 'Enter number greater than %d', 'packeta' ), 0.0 );
414
415 $item = $limit->addText( 'price', __( 'Price', 'packeta' ) . ':' );
416 $item->setRequired();
417 $item->addRule( Form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
418 $item->addRule( Form::MIN, null, 0 );
419 }
420
421 /**
422 * Adds limit fields to form.
423 *
424 * @param Container $surchargeLimits Container.
425 * @param int|string $index Index.
426 *
427 * @return void
428 */
429 private function addSurchargeLimit( Container $surchargeLimits, $index ): void {
430 $limit = $surchargeLimits->addContainer( (string) $index );
431 $item = $limit->addText( 'order_price', __( 'Order price up to', 'packeta' ) . ':' );
432 $item->setRequired();
433 $item->addRule( Form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
434 $item->addRule( Form::MIN, null, 0 );
435 $item->addCondition( Form::MAX, 0 )
436 ->addCondition( Form::MIN, 0 )
437 // translators: %d is the value.
438 ->addRule( Form::BLANK, __( 'Value must not be %d', 'packeta' ), 0 );
439
440 $item = $limit->addText( 'surcharge', __( 'Surcharge', 'packeta' ) . ':' );
441 $item->setRequired();
442 $item->addRule( Form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
443 $item->addRule( Form::MIN, null, 0 );
444 }
445
446 }
447