PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.13.3
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.13.3
3.13.3 3.13.2 3.13.1 3.13.0 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.1 3.12.2 3.12.0 3.11.1 3.11.0 3.10.9 3.10.8 3.10.7 3.10.6 2.8.16 All 261 releases
← All changes | includes/core/rest-api/Controllers/class-settings-controller.php +88 -42 3.12.8 → 3.13.3 View file →
@@ -151,12 +151,12 @@
151 151 )
152 152 );
153 153
154 154 register_rest_route(
155 - $this->namespace, '/' . $this->rest_funnel_settings . '/update-funnel-type' , array(
155 + $this->namespace, '/' . $this->rest_funnel_settings . '/update-builder-type' , array(
156 156 array(
157 157 'methods' => 'POST',
158 - 'callback' => array( $this, 'update_funnel_type' ),
158 + 'callback' => array( $this, 'update_builder_type' ),
159 159 'permission_callback' => array( $this, 'update_items_permissions_check' ),
160 160 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
161 161 )
162 162 )
@@ -162,12 +162,12 @@
162 162 )
163 163 );
164 164
165 165 register_rest_route(
166 - $this->namespace, '/' . $this->rest_funnel_settings . '/update-builder-type' , array(
166 + $this->namespace, '/' . $this->rest_funnel_settings . '/get-funnel-type-id/(?P<funnel_id>[\w-]+)' , array(
167 167 array(
168 - 'methods' => 'POST',
169 - 'callback' => array( $this, 'update_builder_type' ),
168 + 'methods' => 'GET',
169 + 'callback' => array( $this, 'get_funnel_type_id' ),
170 170 'permission_callback' => array( $this, 'update_items_permissions_check' ),
171 171 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
172 172 )
173 173 )
@@ -173,12 +173,12 @@
173 173 )
174 174 );
175 175
176 176 register_rest_route(
177 - $this->namespace, '/' . $this->rest_funnel_settings . '/get-funnel-type-id/(?P<funnel_id>[\w-]+)' , array(
177 + $this->namespace, '/' . $this->rest_funnel_settings . '/canvas-view-mode' , array(
178 178 array(
179 - 'methods' => 'GET',
180 - 'callback' => array( $this, 'get_funnel_type_id' ),
179 + 'methods' => 'POST',
180 + 'callback' => array( $this, 'update_canvas_view_mode' ),
181 181 'permission_callback' => array( $this, 'update_items_permissions_check' ),
182 182 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
183 183 )
184 184 )
@@ -197,8 +197,11 @@
197 197 public function get_group_settings( $group_id ) {
198 198 if ( empty( $group_id ) ) {
199 199 return new WP_Error( 'rest_setting_setting_group_invalid', __( 'Invalid setting group.', 'wpfnl' ), array( 'status' => 404 ) );
200 200 }
201 + if ( ! $this->is_allowed_settings_group( $group_id ) ) {
202 + return new WP_Error( 'rest_setting_setting_group_invalid', __( 'Invalid setting group.', 'wpfnl' ), array( 'status' => 403 ) );
203 + }
201 204 $settings = Wpfnl_functions::get_admin_settings( $group_id );
202 205 if ( empty( $settings ) ) {
203 206 return new WP_Error( 'rest_setting_setting_group_invalid', __( 'Invalid setting group.', 'wpfnl' ), array( 'status' => 404 ) );
204 207 }
@@ -408,8 +411,49 @@
408 411 }
409 412
410 413
411 414 /**
415 + * Validate a settings group_id against WPFunnels-owned option keys.
416 + *
417 + * The `group_id` path parameter is passed straight into get_option()/update_option(),
418 + * so it MUST be restricted to plugin-owned option names. Without this guard a request
419 + * could target arbitrary WordPress options (e.g. `wp_user_roles`) and escalate privileges.
420 + *
421 + * @param string $group_id Option key supplied via the route.
422 + *
423 + * @return bool True when the key is a WPFunnels-owned option, false otherwise.
424 + * @since 3.12.9
425 + */
426 + protected function is_allowed_settings_group( $group_id ) {
427 + if ( ! is_string( $group_id ) || '' === $group_id ) {
428 + return false;
429 + }
430 +
431 + $allowed = array(
432 + '_wpfunnels_general_settings',
433 + '_wpfunnels_permalink_settings',
434 + '_wpfunnels_optin_settings',
435 + '_wpfunnels_advanced_settings',
436 + '_wpfunnels_integrations',
437 + '_wpfunnels_notification_settings',
438 + '_wpfunnels_lms_settings',
439 + '_wpfunnels_recaptcha_setting',
440 + '_wpfunnels_google_map_api_key',
441 + '_wpfunnels_user_roles',
442 + );
443 +
444 + if ( in_array( $group_id, $allowed, true ) ) {
445 + return true;
446 + }
447 +
448 + // Defense in depth: only allow plugin-prefixed option keys so add-ons that
449 + // register their own `_wpfunnels_*` settings groups keep working, while any
450 + // WordPress core / third-party option name is rejected.
451 + return 0 === strpos( $group_id, '_wpfunnels_' );
452 + }
453 +
454 +
455 + /**
412 456 * Update a single setting in a group.
413 457 *
414 458 * @param WP_REST_Request $request Request data.
415 459 *
@@ -417,8 +461,11 @@
417 461 * @since 1.0.0
418 462 */
419 463 public function update_settings( $request ) {
420 464 if( isset( $request['settings_id'], $request['group_id'] )){
465 + if ( ! $this->is_allowed_settings_group( $request['group_id'] ) ) {
466 + return new WP_Error( 'rest_setting_group_invalid', __( 'Invalid setting group.', 'wpfnl' ), array( 'status' => 403 ) );
467 + }
421 468 if( $request['settings_id'] === 'permalink') {
422 469 $default = Wpfnl_functions::get_permalink_settings();
423 470 $settings = Wpfnl_functions::get_admin_settings( '_wpfunnels_permalink_settings', $default);
424 471 } elseif( $request['settings_id'] === 'optin') {
@@ -423,14 +470,10 @@
423 470 $settings = Wpfnl_functions::get_admin_settings( '_wpfunnels_permalink_settings', $default);
424 471 } elseif( $request['settings_id'] === 'optin') {
425 472 $default = Wpfnl_functions::get_optin_settings();
426 473 $settings = Wpfnl_functions::get_admin_settings( '_wpfunnels_optin_settings', $default);
427 - } elseif ($request['settings_id'] === 'funnel_type') {
474 + } else {
428 475 $default = Wpfnl_functions::get_general_settings();
429 - $settings = Wpfnl_functions::get_admin_settings( $request['group_id'], $default);
430 - }
431 - else {
432 - $default = Wpfnl_functions::get_general_settings();
433 476 $settings = Wpfnl_functions::get_admin_settings( $request['group_id'], $default );
434 477 }
435 478
436 479 if ( !$settings ) {
@@ -451,14 +494,9 @@
451 494 Wpfnl_functions::update_admin_settings('_wpfunnels_permalink_saved', true);
452 495 Wpfnl_functions::update_admin_settings($request['group_id'], $settings);
453 496
454 497 }else {
455 - if( 'funnel_type' === $request['settings_id'] ) {
456 - $general_settings = get_option( '_wpfunnels_general_settings' );
457 - $settings[$request['settings_id']] = isset($general_settings['funnel_type']) ? $general_settings['funnel_type'] : 'sales';
458 - }else{
459 - $settings[$request['settings_id']] = $request['value'];
460 - }
498 + $settings[$request['settings_id']] = $request['value'];
461 499
462 500 Wpfnl_functions::update_admin_settings($request['group_id'], $settings);
463 501
464 502 }
@@ -496,9 +534,9 @@
496 534 }
497 535
498 536
499 537 /**
500 - * Update funnel type
538 + * Update funnel builder
501 539 *
502 540 * @param Array $request
503 541 *
504 542 * @return Array
@@ -503,18 +541,18 @@
503 541 *
504 542 * @return Array
505 543 * @since 2.5.3
506 544 */
507 - public function update_funnel_type( $request ){
545 + public function update_builder_type( $request ){
508 546
509 547 $response = [
510 548 'success' => false,
511 549 ];
512 550
513 - if( $request['type'] ){
514 - $type = $request['type'];
551 + if( $request['builder'] ){
552 + $builder = $request['builder'];
515 553 $general_settings = get_option( '_wpfunnels_general_settings' );
516 - $general_settings['funnel_type'] = $type;
554 + $general_settings['builder'] = $builder;
517 555 update_option('_wpfunnels_general_settings', $general_settings );
518 556 $response = [
519 557 'success' => true,
520 558 ];
@@ -523,31 +561,39 @@
523 561 }
524 562
525 563
526 564 /**
527 - * Update funnel builder
565 + * Remember which canvas layout the current user last switched to.
528 566 *
529 - * @param Array $request
567 + * Stored per user rather than in the shared settings, so one admin flipping
568 + * the view switcher does not change the canvas for the rest of the team. The
569 + * site-wide default in Settings still applies to anyone who never switched.
530 570 *
531 - * @return Array
532 - * @since 2.5.3
571 + * @param WP_REST_Request $request The REST request object.
572 + *
573 + * @return WP_REST_Response
574 + * @since 3.12.13
533 575 */
534 - public function update_builder_type( $request ){
576 + public function update_canvas_view_mode( $request ) {
577 + $mode = isset( $request['mode'] ) ? sanitize_text_field( $request['mode'] ) : '';
578 + $user_id = get_current_user_id();
535 579
536 - $response = [
537 - 'success' => false,
538 - ];
580 + if ( ! $user_id || ! in_array( $mode, Wpfnl_functions::get_funnel_builder_modes(), true ) ) {
581 + return rest_ensure_response( array( 'success' => false ) );
582 + }
539 583
540 - if( $request['builder'] ){
541 - $builder = $request['builder'];
542 - $general_settings = get_option( '_wpfunnels_general_settings' );
543 - $general_settings['builder'] = $builder;
544 - update_option('_wpfunnels_general_settings', $general_settings );
545 - $response = [
584 + update_user_meta( $user_id, Wpfnl_functions::CANVAS_VIEW_MODE_META_KEY, $mode );
585 +
586 + if ( 'vertical' === $mode ) {
587 + do_action( 'wpfunnels_vertical_mode_used' );
588 + }
589 +
590 + return rest_ensure_response(
591 + array(
546 592 'success' => true,
547 - ];
548 - }
549 - return rest_ensure_response( $response );
593 + 'mode' => $mode,
594 + )
595 + );
550 596 }
551 597
552 598
553 599 /**
@@ -588,9 +634,9 @@
588 634 if( !isset( $request['name'],$request['value'] )){
589 635 rest_ensure_response( $response );
590 636 }
591 637
592 - if( 'builder' === $request['name'] || 'funnel_type' === $request['name'] ){
638 + if( 'builder' === $request['name'] ){
593 639 // Get and process the value using the appropriate method.
594 640 $name = sanitize_text_field($request['name']);
595 641 $value = sanitize_text_field($request['value']);
596 642