PluginProbe ʕ •ᴥ•ʔ
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments / 4.5.0
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments v4.5.0
4.5.1 4.5.0 4.4.2 4.4.1 4.4.0 4.3.3 4.3.2 4.3.1 4.3.0 4.2.3 4.2.2 4.2.1 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.11.1 1.11.2 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.9.0 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 2.0.0 2.0.1 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.10.0 2.10.1 2.11.0 2.11.1 2.11.2 2.11.3 2.11.4 2.12.0 2.13.0 2.14.0 2.14.1 2.15.0 2.15.1 2.16.0 2.16.1 2.16.2 2.16.3 2.17.0 2.17.1 2.17.2 2.18.0 2.19.0 2.19.2 2.19.3 2.19.4 2.2.0 2.2.1 2.20.0 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.0 2.22.0 2.22.1 2.23.0 2.24.0 2.25.0 2.25.1 2.25.2 2.26.0 2.27.0 2.27.1 2.28.0 2.29.0 2.29.1 2.29.2 2.29.3 2.29.4 2.3.0 2.3.1 2.30.0 2.31.0 2.31.1 2.31.2 2.31.3 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.40.0 2.40.1 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.9.0 3.0.0 3.0.0-RC1 3.0.0-RC2 3.0.0-beta1 3.0.0-beta2 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.10.0 3.10.1 3.11.0 3.12.0 3.13.0 3.13.1 3.13.2 3.13.3 3.13.4 3.14.0 3.15.0 3.15.1 3.15.2 3.15.3 3.15.4 3.15.5 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.16.6 3.16.7 3.16.8 3.17.0 3.17.1 3.17.2 3.17.3 3.17.4 3.17.5 3.17.6 3.18.0 3.19.0 3.19.1 3.19.2 3.2.0 3.2.1 3.2.2 3.20.0 3.20.1 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 4.0.0 4.0.1 4.0.2 4.0.3 trunk 4.1.0 0.2.19.1 4.1.1 1.0.0 4.2.0 1.0.1 1.0.2
surecart / app / src / Settings / SettingService.php
surecart / app / src / Settings Last commit date
RegisterSettingService.php 3 months ago SettingService.php 2 days ago SettingsServiceProvider.php 2 months ago
SettingService.php
633 lines
1 <?php
2
3 namespace SureCart\Settings;
4
5 use SureCart\WordPress\RecaptchaValidationService;
6 use SureCart\Routing\PermalinksSettingsService;
7
8 /**
9 * A service for registering settings.
10 */
11 class SettingService {
12 /**
13 * Per-user meta key for the "Introducing Modern View" onboarding modal
14 * dismissal flag. Stored as user_meta (not a site option) so each admin
15 * dismisses for themselves — onboarding is inherently per-user.
16 */
17 const MODERN_VIEW_INTRO_DISMISSED_META = 'surecart_modern_view_intro_dismissed';
18
19 /**
20 * Boostrap settings.
21 *
22 * @return void
23 */
24 public function bootstrap() {
25 add_action( 'init', [ $this, 'registerSettings' ] );
26 add_action( 'admin_post_sc_set_enhanced_admin_views', [ $this, 'handleSetEnhancedAdminViews' ] );
27 add_action( 'wp_ajax_sc_dismiss_modern_view_intro', [ $this, 'handleDismissModernViewIntro' ] );
28 }
29
30 /**
31 * Handle setting enhanced admin views.
32 *
33 * @return void
34 */
35 public function handleSetEnhancedAdminViews() {
36 check_admin_referer( 'sc_set_enhanced_admin_views' );
37
38 if ( ! current_user_can( 'manage_options' ) ) {
39 wp_die( esc_html__( 'You do not have permission to change this setting.', 'surecart' ), '', [ 'response' => 403 ] );
40 }
41
42 $value = isset( $_POST['value'] ) ? (bool) absint( wp_unslash( $_POST['value'] ) ) : false;
43 update_option( 'surecart_enhanced_admin_views', $value );
44
45 $redirect = isset( $_POST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_POST['redirect_to'] ) ) : admin_url();
46 wp_safe_redirect( $redirect );
47 exit;
48 }
49
50 /**
51 * Mark the Modern View intro modal as dismissed for the current user.
52 *
53 * @return void
54 */
55 public function handleDismissModernViewIntro() {
56 check_admin_referer( 'sc_dismiss_modern_view_intro' );
57
58 if ( ! is_user_logged_in() ) {
59 wp_send_json_error( [ 'message' => __( 'Not logged in.', 'surecart' ) ], 401 );
60 }
61
62 update_user_meta( get_current_user_id(), self::MODERN_VIEW_INTRO_DISMISSED_META, 1 );
63
64 wp_send_json_success( [ 'dismissed' => true ] );
65 }
66
67 /**
68 * Whether the current user has dismissed the Modern View intro modal.
69 *
70 * @return bool
71 */
72 public static function isModernViewIntroDismissed(): bool {
73 if ( ! is_user_logged_in() ) {
74 return false;
75 }
76 return (bool) get_user_meta( get_current_user_id(), self::MODERN_VIEW_INTRO_DISMISSED_META, true );
77 }
78
79 /**
80 * Build the `modern_view_intro` data bag consumed by the React modal.
81 *
82 * @return array
83 */
84 public static function getModernViewIntroData(): array {
85 return [
86 'enabled' => (bool) get_option( 'surecart_enhanced_admin_views', true ),
87 'dismissed' => self::isModernViewIntroDismissed(),
88 'image_url' => trailingslashit( plugin_dir_url( SURECART_PLUGIN_FILE ) ) . 'images/dataview/modern-view-change.svg',
89 'toggle_id' => 'sc-enhanced-views-toggle',
90 'dismiss_url' => admin_url( 'admin-ajax.php' ),
91 'dismiss_nonce' => wp_create_nonce( 'sc_dismiss_modern_view_intro' ),
92 ];
93 }
94
95 /**
96 * Register settings.
97 *
98 * @return void
99 */
100 public function registerSettings() {
101 $this->register(
102 'surecart',
103 'auto_sync_user_to_customer',
104 [
105 'type' => 'boolean',
106 'show_in_rest' => true,
107 'sanitize_callback' => 'boolval',
108 'default' => false,
109 ]
110 );
111 $this->register(
112 'surecart',
113 'honeypot_enabled',
114 [
115 'type' => 'boolean',
116 'show_in_rest' => true,
117 'sanitize_callback' => 'boolval',
118 'default' => true,
119 ]
120 );
121 $this->register(
122 'surecart',
123 'recaptcha_enabled',
124 [
125 'type' => 'boolean',
126 'show_in_rest' => true,
127 'sanitize_callback' => 'boolval',
128 ]
129 );
130 $this->register(
131 'surecart',
132 'recaptcha_site_key',
133 [
134 'type' => 'string',
135 'show_in_rest' => true,
136 'sanitize_callback' => 'sanitize_text_field',
137 ]
138 );
139 $this->register(
140 'surecart',
141 'recaptcha_secret_key',
142 [
143 'type' => 'string',
144 'show_in_rest' => true,
145 'sanitize_callback' => 'sanitize_text_field',
146 ]
147 );
148 $this->register(
149 'surecart',
150 'recaptcha_min_score',
151 [
152 'type' => 'number',
153 'show_in_rest' => true,
154 'default' => 0.5,
155 'sanitize_callback' => 'sanitize_text_field',
156 ]
157 );
158 $this->register(
159 'surecart',
160 'load_stripe_js',
161 [
162 'type' => 'boolean',
163 'show_in_rest' => true,
164 'sanitize_callback' => 'boolval',
165 ]
166 );
167 $this->register(
168 'surecart',
169 'google_map_api_key_enabled',
170 [
171 'type' => 'boolean',
172 'show_in_rest' => true,
173 'sanitize_callback' => 'boolval',
174 ]
175 );
176 $this->register(
177 'surecart',
178 'google_map_api_key',
179 [
180 'type' => 'string',
181 'show_in_rest' => true,
182 'sanitize_callback' => 'sanitize_text_field',
183 ]
184 );
185 $this->register(
186 'surecart',
187 'tracking_confirmation',
188 [
189 'type' => 'boolean',
190 'show_in_rest' => true,
191 'sanitize_callback' => 'boolval',
192 ]
193 );
194 $this->register(
195 'surecart',
196 'tracking_confirmation_message',
197 [
198 'type' => 'string',
199 'show_in_rest' => true,
200 'default' => esc_html__( 'Your email and cart are saved so we can send email reminders about this order.', 'surecart' ),
201 'sanitize_callback' => 'sanitize_text_field',
202 ]
203 );
204 $this->register(
205 'surecart',
206 'buy_link_logo_width',
207 [
208 'type' => 'string',
209 'show_in_rest' => true,
210 'default' => '180px',
211 'sanitize_callback' => 'sanitize_text_field',
212 ]
213 );
214 $this->register(
215 'surecart',
216 'cart_menu_alignment',
217 [
218 'type' => 'string',
219 'show_in_rest' => true,
220 'sanitize_callback' => 'sanitize_text_field',
221 'default' => 'right',
222 ]
223 );
224 $this->register(
225 'surecart',
226 'cart_menu_always_shown',
227 [
228 'type' => 'boolean',
229 'show_in_rest' => true,
230 'sanitize_callback' => 'boolval',
231 'default' => true,
232 ]
233 );
234 $this->register(
235 'surecart',
236 'cart_menu_selected_ids',
237 [
238 'type' => 'array',
239 'items' => 'integer',
240 'show_in_rest' => [
241 'schema' => [
242 'type' => 'array',
243 'items' => [
244 'type' => 'integer',
245 ],
246 ],
247 ],
248 ]
249 );
250 $this->register(
251 'surecart',
252 'cart_icon',
253 [
254 'type' => 'string',
255 'show_in_rest' => true,
256 'sanitize_callback' => 'sanitize_text_field',
257 'default' => 'shopping-bag', // shopping-bag, shopping-cart.
258 ]
259 );
260 $this->register(
261 'surecart',
262 'cart_icon_type',
263 [
264 'type' => 'string',
265 'show_in_rest' => true,
266 'sanitize_callback' => 'sanitize_text_field',
267 'default' => 'floating_icon', // both, floating_icon, menu_icon.
268 ]
269 );
270 $this->register(
271 'surecart',
272 'admin_toolbar_disabled',
273 [
274 'type' => 'boolean',
275 'show_in_rest' => true,
276 'sanitize_callback' => 'boolval',
277 'default' => false,
278 ]
279 );
280 $this->register(
281 'surecart',
282 'password_validation_enabled',
283 [
284 'type' => 'boolean',
285 'show_in_rest' => true,
286 'sanitize_callback' => 'boolval',
287 'default' => false,
288 ]
289 );
290 $this->register(
291 'surecart',
292 'checkout_show_login_prompt',
293 [
294 'type' => 'boolean',
295 'show_in_rest' => true,
296 'sanitize_callback' => 'boolval',
297 'default' => true,
298 ]
299 );
300 $this->register(
301 'surecart',
302 'checkout_geo_capture_title',
303 [
304 'type' => 'string',
305 'show_in_rest' => true,
306 'sanitize_callback' => 'sanitize_text_field',
307 ]
308 );
309 $this->register(
310 'surecart',
311 'checkout_geo_capture_content',
312 [
313 'type' => 'string',
314 'show_in_rest' => true,
315 'sanitize_callback' => [ $this, 'sanitizeRichContent' ],
316 ]
317 );
318 $this->register(
319 'surecart',
320 'checkout_geo_capture_allow_label',
321 [
322 'type' => 'string',
323 'show_in_rest' => true,
324 'sanitize_callback' => 'sanitize_text_field',
325 ]
326 );
327 $this->register(
328 'surecart',
329 'checkout_geo_capture_decline_label',
330 [
331 'type' => 'string',
332 'show_in_rest' => true,
333 'sanitize_callback' => 'sanitize_text_field',
334 ]
335 );
336 $this->register(
337 'surecart',
338 'shop_admin_menu',
339 [
340 'type' => 'boolean',
341 'show_in_rest' => true,
342 'sanitize_callback' => 'boolval',
343 'default' => true,
344 ]
345 );
346 $this->register(
347 'surecart',
348 'cart_admin_menu',
349 [
350 'type' => 'boolean',
351 'show_in_rest' => true,
352 'sanitize_callback' => 'boolval',
353 'default' => true,
354 ]
355 );
356 $this->register(
357 'surecart',
358 'checkout_admin_menu',
359 [
360 'type' => 'boolean',
361 'show_in_rest' => true,
362 'sanitize_callback' => 'boolval',
363 'default' => true,
364 ]
365 );
366 $this->register(
367 'surecart',
368 'dashboard_admin_menu',
369 [
370 'type' => 'boolean',
371 'show_in_rest' => true,
372 'sanitize_callback' => 'boolval',
373 'default' => true,
374 ]
375 );
376 $this->register(
377 'surecart',
378 'unrestricted_test_mode',
379 [
380 'type' => 'boolean',
381 'show_in_rest' => true,
382 'sanitize_callback' => 'boolval',
383 ]
384 );
385 $this->register(
386 'surecart',
387 'hide_help_widget',
388 [
389 'type' => 'boolean',
390 'show_in_rest' => true,
391 'sanitize_callback' => 'boolval',
392 'default' => false,
393 ]
394 );
395 $this->register(
396 'surecart',
397 'currency_switcher_alignment',
398 [
399 'type' => 'string',
400 'show_in_rest' => true,
401 'sanitize_callback' => 'sanitize_text_field',
402 'default' => 'right',
403 ]
404 );
405 $this->register(
406 'surecart',
407 'currency_switcher_selected_ids',
408 [
409 'type' => 'array',
410 'items' => 'integer',
411 'show_in_rest' => [
412 'schema' => [
413 'type' => 'array',
414 'items' => [
415 'type' => 'integer',
416 ],
417 ],
418 ],
419 ]
420 );
421 $this->register(
422 'surecart',
423 'currency_geolocation_enabled',
424 [
425 'type' => 'boolean',
426 'show_in_rest' => true,
427 'sanitize_callback' => 'boolval',
428 'default' => true,
429 ]
430 );
431 $this->register(
432 'surecart',
433 'currency_locale',
434 [
435 'type' => 'string',
436 'show_in_rest' => true,
437 'sanitize_callback' => 'sanitize_text_field',
438 ]
439 );
440 $this->register(
441 'surecart',
442 'hide_verified_buyer_badge',
443 [
444 'type' => 'boolean',
445 'show_in_rest' => true,
446 'sanitize_callback' => 'boolval',
447 'default' => false,
448 ]
449 );
450 $this->register(
451 'surecart',
452 'learn_admin_menu',
453 [
454 'type' => 'boolean',
455 'show_in_rest' => true,
456 'sanitize_callback' => 'boolval',
457 'default' => true,
458 ]
459 );
460 $this->register(
461 'surecart',
462 'learn_completed_steps',
463 [
464 'type' => 'array',
465 'show_in_rest' => [
466 'schema' => [
467 'type' => 'array',
468 'items' => [
469 'type' => 'string',
470 ],
471 ],
472 ],
473 'default' => [],
474 'autoload' => false,
475 'sanitize_callback' => function ( $value ) {
476 if ( ! is_array( $value ) ) {
477 return [];
478 }
479 return array_values(
480 array_unique(
481 array_filter(
482 array_map( 'sanitize_key', $value )
483 )
484 )
485 );
486 },
487 ]
488 );
489 $this->register(
490 'surecart',
491 'learn_total_steps',
492 [
493 'type' => 'integer',
494 'show_in_rest' => true,
495 'sanitize_callback' => 'absint',
496 'default' => 24,
497 'autoload' => false,
498 ]
499 );
500 $this->register(
501 'surecart',
502 'mcp_abilities_enabled',
503 [
504 'type' => 'boolean',
505 'show_in_rest' => true,
506 'sanitize_callback' => 'boolval',
507 'default' => true,
508 'autoload' => false,
509 ]
510 );
511 $this->register(
512 'surecart',
513 'mcp_edit_abilities_enabled',
514 [
515 'type' => 'boolean',
516 'show_in_rest' => true,
517 'sanitize_callback' => 'boolval',
518 'default' => true,
519 'autoload' => false,
520 ]
521 );
522 $this->register(
523 'surecart',
524 'mcp_delete_abilities_enabled',
525 [
526 'type' => 'boolean',
527 'show_in_rest' => true,
528 'sanitize_callback' => 'boolval',
529 'default' => true,
530 'autoload' => false,
531 ]
532 );
533 $this->register(
534 'surecart',
535 'enhanced_admin_views',
536 [
537 'type' => 'boolean',
538 'show_in_rest' => true,
539 'sanitize_callback' => 'boolval',
540 'default' => true,
541 'autoload' => true,
542 ]
543 );
544 }
545 /**
546 * Register a setting.
547 *
548 * @param string $option_group A settings group name. Should correspond to an allowed option key name.
549 * Default allowed option key names include 'surecart', 'discussion', 'media',
550 * 'reading', 'writing', and 'options'.
551 * @param string $option_name The name of an option to sanitize and save.
552 * @param array $args {
553 * Data used to describe the setting when registered.
554 *
555 * @type string $type The type of data associated with this setting.
556 * Valid values are 'string', 'boolean', 'integer', 'number', 'array', and 'object'.
557 * @type string $description A description of the data attached to this setting.
558 * @type callable $sanitize_callback A callback function that sanitizes the option's value.
559 * @type bool|array $show_in_rest Whether data associated with this setting should be included in the REST API.
560 * When registering complex settings, this argument may optionally be an
561 * array with a 'schema' key.
562 * @type mixed $default Default value when calling `get_option()`.
563 */
564 public function register( $option_group, $option_name, $args = [] ) {
565 $service = new RegisterSettingService( $option_group, $option_name, $args );
566 return $service->register();
567 }
568
569 /**
570 * Sanitize rich (post) content, normalizing "visually empty" editor output
571 * (e.g. "<p></p>") to an empty string.
572 *
573 * The block/rich text editor submits "<p></p>" for an empty field, which
574 * wp_kses_post preserves as a non-empty string. That breaks empty-value
575 * default fallbacks. Collapsing it to '' keeps the emptiness contract
576 * consistent with the plain-text settings sanitized via sanitize_text_field.
577 *
578 * @param string $value Raw setting value.
579 * @return string Sanitized content, or '' when visually empty.
580 */
581 public function sanitizeRichContent( $value ) {
582 $value = wp_kses_post( (string) $value );
583
584 // Keep media-only content (no text) as non-empty.
585 if ( preg_match( '/<(img|iframe|video|audio|embed|object|svg)\b/i', $value ) ) {
586 return $value;
587 }
588
589 // Collapse "<p></p>", "<p><br></p>", "&nbsp;" and whitespace to ''.
590 $text = trim( wp_strip_all_tags( str_replace( '&nbsp;', ' ', $value ) ) );
591
592 return '' === $text ? '' : $value;
593 }
594
595 /**
596 * Recaptcha service.
597 *
598 * @return RecaptchaValidationService
599 */
600 public function recaptcha() {
601 return new RecaptchaValidationService();
602 }
603
604 /**
605 * Get the option.
606 *
607 * @return mixed
608 */
609 public function get( $name, $default = false ) {
610 return get_option( 'surecart_' . $name, $default );
611 }
612
613 /**
614 * Get the number of remaining learn steps.
615 *
616 * @return int
617 */
618 public function getLearnRemainingSteps() {
619 $total = (int) $this->get( 'learn_total_steps', 24 );
620 $completed = $this->get( 'learn_completed_steps', [] );
621 return max( 0, $total - count( (array) $completed ) );
622 }
623
624 /**
625 * Get the permalinks settings.
626 *
627 * @return PermalinksSettingsService
628 */
629 public function permalinks() {
630 return new PermalinksSettingsService();
631 }
632 }
633