PluginProbe
seQura / 4.3.2
seQura v4.3.2
4.3.4 4.3.3 4.3.2 4.3.1 trunk 2.0.0 2.0.10 2.0.11 2.0.12 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 3.0.0 3.0.2 3.0.5 3.0.6 3.0.7 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 4.0.0 All 30 releases
sequra / src / Controllers / Rest / class-onboarding-rest-controller.php

class-onboarding-rest-controller.php in seQura 4.3.2, at src/Controllers/Rest/class-onboarding-rest-controller.php

626 lines 21.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * REST Onboarding Controller
4 *
5 * @package SeQura/WC
6 * @subpackage SeQura/WC/Controllers/Rest
7 */
8
9 namespace SeQura\WC\Controllers\Rest;
10
11 use SeQura\Core\BusinessLogic\AdminAPI\AdminAPI;
12 use SeQura\Core\BusinessLogic\AdminAPI\Connection\Requests\ConnectionRequest;
13 use SeQura\Core\BusinessLogic\AdminAPI\Connection\Requests\OnboardingRequest;
14 use SeQura\Core\BusinessLogic\AdminAPI\CountryConfiguration\Requests\CountryConfigurationRequest;
15 use SeQura\Core\BusinessLogic\AdminAPI\Disconnect\Requests\DisconnectRequest;
16 use SeQura\Core\BusinessLogic\AdminAPI\PromotionalWidgets\Requests\WidgetSettingsRequest;
17 use SeQura\Core\BusinessLogic\AdminAPI\Response\Response;
18 use SeQura\WC\Services\Log\Interface_Logger_Service;
19 use SeQura\Core\Infrastructure\Utility\RegexProvider;
20 use WP_Error;
21 use WP_REST_Request;
22 use WP_REST_Response;
23
24 /**
25 * REST Onboarding Controller
26 */
27 class Onboarding_REST_Controller extends REST_Controller {
28
29 private const PARAM_ENVIRONMENT = 'environment';
30 private const PARAM_USERNAME = 'username';
31 private const PARAM_PASSWORD = 'password';
32 private const PARAM_DEPLOYMENT_ID = 'deploymentId';
33 private const PARAM_DEPLOYMENT = 'deployment';
34 private const PARAM_CONNECTION_DATA = 'connectionData';
35 private const PARAM_SEND_STATISTICAL_DATA = 'sendStatisticalData';
36 private const PARAM_DISPLAY_WIDGET_ON_PRODUCT_PAGE = 'displayWidgetOnProductPage';
37 private const PARAM_SHOW_INSTALLMENT_AMOUNT_IN_PRODUCT_LISTING = 'showInstallmentAmountInProductListing';
38 private const PARAM_SHOW_INSTALLMENT_AMOUNT_IN_CART_PAGE = 'showInstallmentAmountInCartPage';
39 private const PARAM_WIDGET_STYLES = 'widgetStyles';
40 private const PARAM_CUSTOM_LOCATIONS = 'customLocations';
41 private const PARAM_CUSTOM_LOCATION_SEL_FOR_TARGET = 'selForTarget';
42 private const PARAM_CUSTOM_LOCATION_WIDGET_STYLES = self::PARAM_WIDGET_STYLES;
43 private const PARAM_CUSTOM_LOCATION_DISPLAY_WIDGET = 'displayWidget';
44 private const PARAM_CUSTOM_LOCATION_PRODUCT = 'product';
45 private const PARAM_CUSTOM_LOCATION_CAMPAIGN = 'campaign';
46
47 private const PARAM_SEL_FOR_PRICE = 'productPriceSelector';
48 private const PARAM_SEL_FOR_ALT_PRICE = 'altProductPriceSelector';
49 private const PARAM_SEL_FOR_ALT_PRICE_TRIGGER = 'altProductPriceTriggerSelector';
50 private const PARAM_SEL_FOR_DEFAULT_LOCATION = 'defaultProductLocationSelector';
51 // Cart widget config.
52 private const PARAM_CART_SEL_FOR_PRICE = 'cartPriceSelector';
53 private const PARAM_CART_SEL_FOR_DEFAULT_LOCATION = 'cartLocationSelector';
54 private const PARAM_CART_WIDGET_ON_PAGE = 'widgetOnCartPage';
55 // Product listing widget config.
56 private const PARAM_LISTING_SEL_FOR_PRICE = 'listingPriceSelector';
57 private const PARAM_LISTING_SEL_FOR_LOCATION = 'listingLocationSelector';
58 private const PARAM_LISTING_WIDGET_ON_PAGE = 'widgetOnListingPage';
59
60 private const PARAM_IS_FULL_DISCONNECT = 'isFullDisconnect';
61
62 /**
63 * Constructor.
64 *
65 * @param string $rest_namespace The namespace.
66 * @param Interface_Logger_Service $logger The logger service.
67 * @param RegexProvider $regex The regex provider.
68 */
69 public function __construct(
70 $rest_namespace,
71 Interface_Logger_Service $logger,
72 RegexProvider $regex
73 ) {
74 parent::__construct( $logger, $regex );
75 $this->namespace = $rest_namespace;
76 $this->rest_base = '/onboarding';
77 }
78
79 /**
80 * Register the API endpoints.
81 *
82 * @return void
83 */
84 public function register_routes() {
85 $this->logger->log_debug( 'Hook executed', __FUNCTION__, __CLASS__ );
86 $store_id_args = array( self::PARAM_STORE_ID => $this->get_arg_string() );
87
88 $data_args = array_merge(
89 $store_id_args,
90 array(
91 self::PARAM_ENVIRONMENT => array_merge(
92 $this->get_arg_string(),
93 array( 'enum' => array( 'sandbox', 'production' ) )
94 ),
95 self::PARAM_USERNAME => $this->get_arg_string(),
96 self::PARAM_PASSWORD => $this->get_arg_string(),
97 self::PARAM_SEND_STATISTICAL_DATA => $this->get_arg_bool(),
98 )
99 );
100
101 $disconnect_args = array_merge(
102 $store_id_args,
103 array(
104 self::PARAM_IS_FULL_DISCONNECT => $this->get_arg_bool(),
105 self::PARAM_DEPLOYMENT_ID => $this->get_arg_string(),
106 )
107 );
108
109 $validate_data_args = array_merge(
110 $data_args,
111 array(
112 self::PARAM_MERCHANT_ID => $this->get_arg_string( false ),
113 )
114 );
115
116 $widget_args = array_merge(
117 $store_id_args,
118 array(
119 self::PARAM_DISPLAY_WIDGET_ON_PRODUCT_PAGE => $this->get_arg_bool(),
120 self::PARAM_SHOW_INSTALLMENT_AMOUNT_IN_PRODUCT_LISTING => $this->get_arg_bool(),
121 self::PARAM_SHOW_INSTALLMENT_AMOUNT_IN_CART_PAGE => $this->get_arg_bool(),
122 self::PARAM_WIDGET_STYLES => $this->get_arg_string(),
123 self::PARAM_SEL_FOR_PRICE => $this->get_arg_string( true, '', array( $this, 'validate_required_widget_selector' ) ),
124 self::PARAM_SEL_FOR_ALT_PRICE => $this->get_arg_string( true, '', array( $this, 'validate_optional_widget_selector' ) ),
125 self::PARAM_SEL_FOR_ALT_PRICE_TRIGGER => $this->get_arg_string( true, '', array( $this, 'validate_optional_widget_selector' ) ),
126 self::PARAM_SEL_FOR_DEFAULT_LOCATION => $this->get_arg_string( true, '', array( $this, 'validate_required_widget_selector' ) ),
127
128 self::PARAM_CUSTOM_LOCATIONS => $this->get_arg_widget_location_list(),
129
130 self::PARAM_CART_SEL_FOR_PRICE => $this->get_arg_string( true, '', array( $this, 'validate_required_cart_widget_selector' ) ),
131 self::PARAM_CART_SEL_FOR_DEFAULT_LOCATION => $this->get_arg_string( true, '', array( $this, 'validate_required_cart_widget_selector' ) ),
132 self::PARAM_CART_WIDGET_ON_PAGE => $this->get_arg_string( true, '', array( $this, 'validate_required_cart_widget_selector' ) ),
133
134 self::PARAM_LISTING_SEL_FOR_PRICE => $this->get_arg_string( true, '', array( $this, 'validate_required_listing_widget_selector' ) ),
135 self::PARAM_LISTING_SEL_FOR_LOCATION => $this->get_arg_string( true, '', array( $this, 'validate_required_listing_widget_selector' ) ),
136 self::PARAM_LISTING_WIDGET_ON_PAGE => $this->get_arg_string( true, '', array( $this, 'validate_required_listing_widget_selector' ) ),
137 )
138 );
139
140 $store_id = $this->url_param_pattern( self::PARAM_STORE_ID );
141
142 $this->register_get( "data/{$store_id}", 'get_connection_data', $store_id_args );
143 $this->register_post( "data/validate/{$store_id}", 'validate_connection_data', $validate_data_args );
144 $this->register_post( "data/disconnect/{$store_id}", 'disconnect', $disconnect_args );
145 $this->register_post( "data/connect/{$store_id}", 'connect', $store_id_args );
146
147 $this->register_get( "widgets/{$store_id}", 'get_widgets', $store_id_args );
148 $this->register_post( "widgets/{$store_id}", 'save_widgets', $widget_args );
149
150 $this->register_get( "countries/selling/{$store_id}", 'get_selling_countries', $store_id_args );
151 $this->register_get( "countries/{$store_id}", 'get_countries', $store_id_args );
152 $this->register_post( "countries/{$store_id}", 'save_countries', $store_id_args );
153
154 $this->register_get( "deployments/{$store_id}", 'get_deployments', $store_id_args );
155 $this->register_get( "deployments/not-connected/{$store_id}", 'get_not_connected_deployments', $store_id_args );
156 }
157
158 /**
159 * Get connection data.
160 *
161 * @param WP_REST_Request $request The request.
162 *
163 * @return WP_REST_Response|WP_Error
164 */
165 public function get_connection_data( WP_REST_Request $request ) {
166 /**
167 * Response
168 *
169 * @var Response $response */
170 $response = AdminAPI::get()
171 ->connection( strval( $request->get_param( self::PARAM_STORE_ID ) ) )
172 ->getOnboardingData();
173 return $this->build_response( $response );
174 }
175
176 /**
177 * Get deployments.
178 *
179 * @param WP_REST_Request $request The request.
180 *
181 * @return WP_REST_Response|WP_Error
182 */
183 public function get_deployments( WP_REST_Request $request ) {
184 /**
185 * Response
186 *
187 * @var Response $response */
188 $response = AdminAPI::get()
189 ->deployments( strval( $request->get_param( self::PARAM_STORE_ID ) ) )
190 ->getAllDeployments();
191 return $this->build_response( $response );
192 }
193
194 /**
195 * Get deployments.
196 *
197 * @param WP_REST_Request $request The request.
198 *
199 * @return WP_REST_Response|WP_Error
200 */
201 public function get_not_connected_deployments( WP_REST_Request $request ) {
202 /**
203 * Response
204 *
205 * @var Response $response */
206 $response = AdminAPI::get()
207 ->deployments( strval( $request->get_param( self::PARAM_STORE_ID ) ) )
208 ->getNotConnectedDeployments();
209 return $this->build_response( $response );
210 }
211
212 /**
213 * Validate connection data before saving.
214 *
215 * @param WP_REST_Request $request The request.
216 *
217 * @return WP_REST_Response|WP_Error
218 */
219 public function validate_connection_data( WP_REST_Request $request ) {
220 /**
221 * Response
222 *
223 * @var Response $response */
224 $response = AdminAPI::get()
225 ->connection( strval( $request->get_param( self::PARAM_STORE_ID ) ) )
226 ->isConnectionDataValid(
227 new ConnectionRequest(
228 strval( $request->get_param( self::PARAM_ENVIRONMENT ) ),
229 strval( $request->get_param( self::PARAM_MERCHANT_ID ) ),
230 strval( $request->get_param( self::PARAM_USERNAME ) ),
231 strval( $request->get_param( self::PARAM_PASSWORD ) ),
232 strval( $request->get_param( self::PARAM_DEPLOYMENT_ID ) )
233 )
234 );
235 return $this->build_response( $response );
236 }
237
238 /**
239 * Disconnects integration from the shop.
240 *
241 * @param WP_REST_Request $request The request.
242 *
243 * @return WP_REST_Response|WP_Error
244 */
245 public function disconnect( WP_REST_Request $request ) {
246 /**
247 * Response
248 *
249 * @var Response $response */
250 $response = AdminAPI::get()
251 ->disconnect( strval( $request->get_param( self::PARAM_STORE_ID ) ) )
252 ->disconnect(
253 new DisconnectRequest(
254 strval( $request->get_param( self::PARAM_DEPLOYMENT_ID ) ),
255 (bool) $request->get_param( self::PARAM_IS_FULL_DISCONNECT )
256 )
257 );
258 return $this->build_response( $response );
259 }
260
261 /**
262 * Disconnects integration from the shop.
263 *
264 * @param WP_REST_Request $request The request.
265 *
266 * @return WP_REST_Response|WP_Error
267 */
268 public function connect( WP_REST_Request $request ) {
269 /**
270 * Connection Data
271 *
272 * @var array<array{merchantId?: string,
273 * username?: string,
274 * password?: string,
275 * deployment?: string}> $connection_data_array */
276 $connection_data_array = $request->get_param( self::PARAM_CONNECTION_DATA ) ?? array();
277 $connection_requests = array();
278 foreach ( $connection_data_array as $conn_data ) {
279 $connection_requests[] = new ConnectionRequest(
280 strval( $request->get_param( self::PARAM_ENVIRONMENT ) ),
281 strval( $conn_data[ self::PARAM_MERCHANT_ID ] ?? '' ),
282 strval( $conn_data[ self::PARAM_USERNAME ] ?? '' ),
283 strval( $conn_data[ self::PARAM_PASSWORD ] ?? '' ),
284 strval( $conn_data[ self::PARAM_DEPLOYMENT ] ?? '' )
285 );
286 }
287
288 /**
289 * Response
290 *
291 * @var Response $response */
292 $response = AdminAPI::get()
293 ->connection( strval( $request->get_param( self::PARAM_STORE_ID ) ) )
294 ->connect(
295 new OnboardingRequest(
296 $connection_requests,
297 (bool) $request->get_param( self::PARAM_SEND_STATISTICAL_DATA ),
298 )
299 );
300 return $this->build_response( $response );
301 }
302
303 /**
304 * GET selling countries.
305 *
306 * @param WP_REST_Request $request The request.
307 *
308 * @return WP_REST_Response|WP_Error
309 */
310 public function get_selling_countries( WP_REST_Request $request ) {
311 /**
312 * Response
313 *
314 * @var Response $response */
315 $response = AdminAPI::get()
316 ->countryConfiguration( strval( $request->get_param( self::PARAM_STORE_ID ) ) )
317 ->getSellingCountries();
318 return $this->build_response( $response );
319 }
320
321 /**
322 * GET countries.
323 *
324 * @param WP_REST_Request $request The request.
325 *
326 * @return WP_REST_Response|WP_Error
327 */
328 public function get_countries( WP_REST_Request $request ) {
329 /**
330 * Response
331 *
332 * @var Response $response */
333 $response = AdminAPI::get()
334 ->countryConfiguration( strval( $request->get_param( self::PARAM_STORE_ID ) ) )
335 ->getCountryConfigurations();
336 return $this->build_response( $response );
337 }
338
339 /**
340 * Save countries.
341 *
342 * @throws \Exception
343 * @param WP_REST_Request $request The request.
344 *
345 * @return WP_REST_Response|WP_Error
346 */
347 public function save_countries( WP_REST_Request $request ) {
348 if ( ! $this->validate_countries( $request ) ) {
349 return new WP_REST_Response( 'Invalid data', 400 );
350 }
351 /**
352 * Data
353 *
354 * @var array<int, array<string, string>> $data */
355 $data = (array) json_decode( $request->get_body(), true );
356
357 /**
358 * Response
359 *
360 * @var Response $response */
361 $response = AdminAPI::get()
362 ->countryConfiguration( strval( $request->get_param( self::PARAM_STORE_ID ) ) )
363 ->saveCountryConfigurations( new CountryConfigurationRequest( $data ) );
364 return $this->build_response( $response );
365 }
366
367 /**
368 * GET widgets.
369 *
370 * @param WP_REST_Request $request The request.
371 *
372 * @return WP_REST_Response|WP_Error
373 */
374 public function get_widgets( WP_REST_Request $request ) {
375 /**
376 * Response
377 *
378 * @var Response $response */
379 $response = AdminAPI::get()
380 ->widgetConfiguration( strval( $request->get_param( self::PARAM_STORE_ID ) ) )
381 ->getWidgetSettings();
382
383 return $this->build_response( $response );
384 }
385
386 /**
387 * Save countries.
388 *
389 * @throws \Exception
390 * @param WP_REST_Request $request The request.
391 *
392 * @return WP_REST_Response|WP_Error
393 */
394 public function save_widgets( WP_REST_Request $request ) {
395 /**
396 * Response
397 *
398 * @var Response $response */
399 $response = AdminAPI::get()
400 ->widgetConfiguration( strval( $request->get_param( self::PARAM_STORE_ID ) ) )
401 ->setWidgetSettings(
402 new WidgetSettingsRequest(
403 (bool) $request->get_param( self::PARAM_DISPLAY_WIDGET_ON_PRODUCT_PAGE ),
404 (bool) $request->get_param( self::PARAM_SHOW_INSTALLMENT_AMOUNT_IN_PRODUCT_LISTING ),
405 (bool) $request->get_param( self::PARAM_SHOW_INSTALLMENT_AMOUNT_IN_CART_PAGE ),
406 strval( $request->get_param( self::PARAM_WIDGET_STYLES ) ),
407 strval( $request->get_param( self::PARAM_SEL_FOR_PRICE ) ),
408 strval( $request->get_param( self::PARAM_SEL_FOR_DEFAULT_LOCATION ) ),
409 strval( $request->get_param( self::PARAM_CART_SEL_FOR_PRICE ) ),
410 strval( $request->get_param( self::PARAM_CART_SEL_FOR_DEFAULT_LOCATION ) ),
411 strval( $request->get_param( self::PARAM_CART_WIDGET_ON_PAGE ) ),
412 strval( $request->get_param( self::PARAM_LISTING_WIDGET_ON_PAGE ) ),
413 strval( $request->get_param( self::PARAM_LISTING_SEL_FOR_PRICE ) ),
414 strval( $request->get_param( self::PARAM_LISTING_SEL_FOR_LOCATION ) ),
415 strval( $request->get_param( self::PARAM_SEL_FOR_ALT_PRICE ) ),
416 strval( $request->get_param( self::PARAM_SEL_FOR_ALT_PRICE_TRIGGER ) ),
417 (array) $request->get_param( self::PARAM_CUSTOM_LOCATIONS )
418 )
419 );
420 return $this->build_response( $response );
421 }
422
423 /**
424 * Validate if countries payload is valid.
425 *
426 * @param WP_REST_Request $request The request.
427 */
428 public function validate_countries( WP_REST_Request $request ): bool {
429 $data = json_decode( $request->get_body(), true );
430 if ( ! is_array( $data ) ) {
431 return false;
432 }
433 /**
434 * Response
435 *
436 * @var Response $response */
437 $response = AdminAPI::get()
438 ->countryConfiguration( strval( $request->get_param( self::PARAM_STORE_ID ) ) )
439 ->getSellingCountries();
440
441 if ( ! $response->isSuccessful() ) {
442 return false;
443 }
444
445 $allowed_countries = array_column( $response->toArray(), 'code' );
446
447 foreach ( $data as $country ) {
448 if ( ! isset( $country['countryCode'] )
449 || ! isset( $country['merchantId'] )
450 || ! is_string( $country['countryCode'] )
451 || ! is_string( $country['merchantId'] )
452 || ! in_array( $country['countryCode'], $allowed_countries, true )
453 ) {
454 return false;
455 }
456 }
457 return true;
458 }
459
460 /**
461 * Validate if widget label is valid.
462 *
463 * @param mixed $param The parameter.
464 * @param WP_REST_Request $request The request.
465 * @param string $key The key.
466 */
467 public function validate_widget_labels( $param, $request, $key ): bool {
468 return is_array( $param )
469 && isset( $param['message'] )
470 && is_string( $param['message'] )
471 && isset( $param['messageBelowLimit'] )
472 && is_string( $param['messageBelowLimit'] );
473 }
474
475 /**
476 * Validate if required selector is valid.
477 *
478 * @param mixed $param The parameter.
479 */
480 private function validate_required_selector( string $dependant_param_key, $param, WP_REST_Request $request, string $key ): bool {
481
482 $use_widgets = (bool) $request->get_param( $dependant_param_key );
483 if ( ! $use_widgets ) {
484 return null === $param || is_string( $param );
485 }
486
487 return null !== $param
488 && is_string( $param )
489 && '' !== trim( $param );
490 }
491
492 /**
493 * Validate if required widget selector is valid.
494 *
495 * @param mixed $param The param.
496 */
497 public function validate_required_widget_selector( $param, WP_REST_Request $request, string $key ): bool {
498 return $this->validate_required_selector( self::PARAM_DISPLAY_WIDGET_ON_PRODUCT_PAGE, $param, $request, $key );
499 }
500
501 /**
502 * Validate if required cart widget selector is valid.
503 *
504 * @param mixed $param The param.
505 */
506 public function validate_required_cart_widget_selector( $param, WP_REST_Request $request, string $key ): bool {
507 return $this->validate_required_selector( self::PARAM_SHOW_INSTALLMENT_AMOUNT_IN_CART_PAGE, $param, $request, $key );
508 }
509
510 /**
511 * Validate if required listing widget selector is valid.
512 *
513 * @param mixed $param The param.
514 */
515 public function validate_required_listing_widget_selector( $param, WP_REST_Request $request, string $key ): bool {
516 return $this->validate_required_selector( self::PARAM_SHOW_INSTALLMENT_AMOUNT_IN_PRODUCT_LISTING, $param, $request, $key );
517 }
518
519 /**
520 * Validate if optional widget selector is valid.
521 *
522 * @param mixed $param The parameter.
523 * @param WP_REST_Request $request The request.
524 * @param string $key The key.
525 */
526 public function validate_optional_widget_selector( $param, $request, $key ): bool {
527 return null === $param || is_string( $param );
528 }
529
530 /**
531 * Sanitize widget label.
532 *
533 * @param mixed[] $param The parameter.
534 * @return mixed[]
535 */
536 public function sanitize_widget_labels( $param ): array {
537 return array(
538 'message' => \sanitize_text_field( strval( $param['message'] ) ),
539 'messageBelowLimit' => \sanitize_text_field( strval( $param['messageBelowLimit'] ) ),
540 );
541 }
542
543 /**
544 * Get argument structure for the widget location list.
545 *
546 * @param bool $required If the argument is required.
547 * @param mixed $default_value The default value. Null will be ignored.
548 * @param callable $validate The validate callback. Leave null to use the default.
549 * @param callable $sanitize The sanitize callback. Leave null to use the default.
550 * @return mixed[]
551 */
552 protected function get_arg_widget_location_list( $required = true, $default_value = array(), $validate = null, $sanitize = null ): array {
553 return array_merge(
554 $this->get_arg( $required, $default_value ),
555 array(
556 'validate_callback' => null === $validate ? array( $this, 'validate_widget_location_list' ) : $validate,
557 'sanitize_callback' => null === $sanitize ? array( $this, 'sanitize_widget_location_list' ) : $sanitize,
558 )
559 );
560 }
561
562 /**
563 * Validate if widget location list is valid.
564 *
565 * @param mixed $param The parameter.
566 * @param WP_REST_Request $request The request.
567 * @param string $key The key.
568 */
569 public function validate_widget_location_list( $param, $request, $key ): bool {
570 if ( ! is_array( $param ) ) {
571 return false;
572 }
573 foreach ( $param as $location ) {
574 if ( ! isset(
575 $location[ self::PARAM_CUSTOM_LOCATION_SEL_FOR_TARGET ],
576 $location[ self::PARAM_CUSTOM_LOCATION_PRODUCT ],
577 $location[ self::PARAM_CUSTOM_LOCATION_WIDGET_STYLES ],
578 $location[ self::PARAM_CUSTOM_LOCATION_DISPLAY_WIDGET ]
579 )
580 || ! is_string( $location[ self::PARAM_CUSTOM_LOCATION_SEL_FOR_TARGET ] )
581 || ! is_string( $location[ self::PARAM_CUSTOM_LOCATION_PRODUCT ] )
582 || ! is_string( $location[ self::PARAM_CUSTOM_LOCATION_WIDGET_STYLES ] )
583 || ! is_bool( $location[ self::PARAM_CUSTOM_LOCATION_DISPLAY_WIDGET ] )
584 || empty( $location[ self::PARAM_CUSTOM_LOCATION_PRODUCT ] )
585 ) {
586 return false;
587 }
588
589 // check if exists another location with the same title and product.
590 $product = $location[ self::PARAM_CUSTOM_LOCATION_PRODUCT ];
591 $campaign = $location[ self::PARAM_CUSTOM_LOCATION_CAMPAIGN ] ?? null;
592 $found = \array_filter(
593 $param,
594 function ( $loc ) use ( $product, $campaign ) {
595 return isset( $loc[ self::PARAM_CUSTOM_LOCATION_PRODUCT ] )
596 && $loc[ self::PARAM_CUSTOM_LOCATION_PRODUCT ] === $product
597 && ( $loc[ self::PARAM_CUSTOM_LOCATION_CAMPAIGN ] ?? null ) === $campaign;
598 }
599 );
600 if ( count( $found ) > 1 ) {
601 return false;
602 }
603 }
604 return true;
605 }
606
607 /**
608 * Sanitize widget location list.
609 *
610 * @param array<int, array<string, string>> $param The parameter.
611 * @return array<int, array<string, string>>
612 */
613 public function sanitize_widget_location_list( $param ): array {
614 foreach ( $param as &$location ) {
615 $location = array(
616 self::PARAM_CUSTOM_LOCATION_SEL_FOR_TARGET => \sanitize_text_field( strval( $location[ self::PARAM_CUSTOM_LOCATION_SEL_FOR_TARGET ] ) ),
617 self::PARAM_CUSTOM_LOCATION_PRODUCT => \sanitize_text_field( strval( $location[ self::PARAM_CUSTOM_LOCATION_PRODUCT ] ) ),
618 self::PARAM_CUSTOM_LOCATION_CAMPAIGN => isset( $location[ self::PARAM_CUSTOM_LOCATION_CAMPAIGN ] ) ? \sanitize_text_field( strval( $location[ self::PARAM_CUSTOM_LOCATION_CAMPAIGN ] ) ) : null,
619 self::PARAM_CUSTOM_LOCATION_WIDGET_STYLES => \sanitize_text_field( strval( $location[ self::PARAM_CUSTOM_LOCATION_WIDGET_STYLES ] ) ),
620 self::PARAM_CUSTOM_LOCATION_DISPLAY_WIDGET => (bool) $location[ self::PARAM_CUSTOM_LOCATION_DISPLAY_WIDGET ],
621 );
622 }
623 return $param;
624 }
625 }
626