Make WordPress Core

Changeset 63526


Ignore:
Timestamp:
09/08/2026 08:41:22 AM (less than one hour ago)
Author:
oandregal
Message:

REST API: expose privacy policy page in settings endpoint.

Props oandregal, ntsekouras, joen.
Fixes #66045.

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/default-filters.php

    r63012 r63526  
    546546add_action( 'rest_api_init', 'rest_api_default_filters', 10, 1 );
    547547add_action( 'rest_api_init', 'register_initial_settings', 10 );
     548add_filter( 'rest_pre_update_setting', 'rest_restrict_privacy_policy_page_setting_update', 10, 2 );
    548549add_action( 'rest_api_init', 'create_initial_rest_routes', 99 );
    549550add_action( 'parse_request', 'rest_api_loaded' );
  • trunk/src/wp-includes/option.php

    r63491 r63526  
    27402740 * @since 4.7.0
    27412741 * @since 6.0.1 The `show_on_front`, `page_on_front`, and `page_for_posts` options were added.
     2742 * @since 7.2.0 The `wp_page_for_privacy_policy` option was registered, exposed as `page_for_privacy_policy`.
    27422743 */
    27432744function register_initial_settings() {
     
    29332934
    29342935        register_setting(
     2936                'reading',
     2937                'wp_page_for_privacy_policy',
     2938                array(
     2939                        'show_in_rest' => array(
     2940                                'name' => 'page_for_privacy_policy',
     2941                        ),
     2942                        'type'         => 'integer',
     2943                        'description'  => __( 'The ID of the page that should be displayed as the privacy policy page' ),
     2944                )
     2945        );
     2946
     2947        register_setting(
    29352948                'discussion',
    29362949                'default_ping_status',
  • trunk/src/wp-includes/rest-api.php

    r63509 r63526  
    34483448}
    34493449
     3450/**
     3451 * Prevents users without the `manage_privacy_options` capability from
     3452 * changing the privacy policy page through the REST API.
     3453 *
     3454 * The settings endpoint only checks `manage_options`. On multisite the
     3455 * `manage_privacy_options` capability maps to `manage_network`, so a site
     3456 * administrator can read the setting but must not change it, matching the
     3457 * Settings > Privacy screen.
     3458 *
     3459 * @since 7.2.0
     3460 *
     3461 * @param bool   $updated Whether the setting update has already been handled.
     3462 * @param string $name    Setting name (as shown in REST API responses).
     3463 * @return bool Whether to short-circuit the update.
     3464 */
     3465function rest_restrict_privacy_policy_page_setting_update( $updated, $name ) {
     3466        if ( 'page_for_privacy_policy' === $name && ! current_user_can( 'manage_privacy_options' ) ) {
     3467                return true;
     3468        }
     3469        return $updated;
     3470}
    34503471
    34513472/**
  • trunk/tests/phpunit/tests/rest-api/rest-settings-controller.php

    r63161 r63526  
    5858                }
    5959
     60                remove_filter( 'map_meta_cap', array( $this, 'deny_manage_privacy_options' ), 10 );
     61
    6062                parent::tear_down();
     63        }
     64
     65        /**
     66         * Maps `manage_privacy_options` to `do_not_allow`, as happens for a site
     67         * administrator on multisite.
     68         *
     69         * @param string[] $caps Primitive capabilities required.
     70         * @param string   $cap  Capability being checked.
     71         * @return string[] Primitive capabilities required.
     72         */
     73        public function deny_manage_privacy_options( $caps, $cap ) {
     74                if ( 'manage_privacy_options' === $cap ) {
     75                        return array( 'do_not_allow' );
     76                }
     77                return $caps;
    6178        }
    6279
     
    117134                        'page_on_front',
    118135                        'page_for_posts',
     136                        'page_for_privacy_policy',
    119137                        'default_ping_status',
    120138                        'default_comment_status',
     
    402420                $this->assertSame( 'The new title!', $data['title'] );
    403421                $this->assertSame( get_option( 'blogname' ), $data['title'] );
     422        }
     423
     424        public function test_update_item_privacy_policy_page() {
     425                wp_set_current_user( self::$administrator );
     426                if ( is_multisite() ) {
     427                        grant_super_admin( self::$administrator );
     428                }
     429                $page_id = self::factory()->post->create( array( 'post_type' => 'page' ) );
     430
     431                $request = new WP_REST_Request( 'PUT', '/wp/v2/settings' );
     432                $request->set_param( 'page_for_privacy_policy', $page_id );
     433                $response = rest_get_server()->dispatch( $request );
     434                $data     = $response->get_data();
     435
     436                $this->assertSame( 200, $response->get_status() );
     437                $this->assertSame( $page_id, $data['page_for_privacy_policy'] );
     438                $this->assertSame( $page_id, (int) get_option( 'wp_page_for_privacy_policy' ) );
     439        }
     440
     441        public function test_update_item_privacy_policy_page_without_capability() {
     442                wp_set_current_user( self::$administrator );
     443                $page_id = self::factory()->post->create( array( 'post_type' => 'page' ) );
     444                update_option( 'wp_page_for_privacy_policy', $page_id );
     445                $other_page_id = self::factory()->post->create( array( 'post_type' => 'page' ) );
     446                add_filter( 'map_meta_cap', array( $this, 'deny_manage_privacy_options' ), 10, 2 );
     447
     448                $request = new WP_REST_Request( 'PUT', '/wp/v2/settings' );
     449                $request->set_param( 'page_for_privacy_policy', $other_page_id );
     450                $response = rest_get_server()->dispatch( $request );
     451                $data     = $response->get_data();
     452
     453                $this->assertSame( 200, $response->get_status() );
     454                $this->assertSame( $page_id, $data['page_for_privacy_policy'], 'The response should still report the previous page.' );
     455                $this->assertSame( $page_id, (int) get_option( 'wp_page_for_privacy_policy' ), 'The option should not change.' );
    404456        }
    405457
  • trunk/tests/qunit/fixtures/wp-api-generated.js

    r63200 r63526  
    1123811238                            "required": false
    1123911239                        },
     11240                        "page_for_privacy_policy": {
     11241                            "title": "",
     11242                            "description": "The ID of the page that should be displayed as the privacy policy page",
     11243                            "type": "integer",
     11244                            "required": false
     11245                        },
    1124011246                        "default_ping_status": {
    1124111247                            "title": "",
     
    1493314939    "page_on_front": 0,
    1493414940    "page_for_posts": 0,
     14941    "page_for_privacy_policy": 0,
    1493514942    "default_ping_status": "open",
    1493614943    "default_comment_status": "open",
Note: See TracChangeset for help on using the changeset viewer.