Make WordPress Core

Ticket #43738: 43738-2.diff

File 43738-2.diff, 17.0 KB (added by pbiron, 3 weeks ago)

fix a bug in 43738-1.php, where the $status param was not passed to wp_user_create_request().

  • src/wp-admin/erase-personal-data.php

    From f2c1d99e225dd91932980d7d69cd773a24587a44 Mon Sep 17 00:00:00 2001
    From: Paul Biron <paul@sparrowhawkcomputing.com>
    Date: Sat, 28 Jun 2025 16:21:32 -0600
    Subject: [PATCH] Add UI for managing requests at the network-level.
    
    ---
     src/wp-admin/erase-personal-data.php          |  2 +-
     src/wp-admin/export-personal-data.php         |  2 +-
     .../class-wp-privacy-requests-table.php       | 22 +++++++
     src/wp-admin/includes/privacy-tools.php       | 54 ++++++++++++----
     src/wp-admin/network/erase-personal-data.php  | 12 ++++
     src/wp-admin/network/export-personal-data.php | 12 ++++
     src/wp-admin/network/menu.php                 |  5 ++
     src/wp-includes/admin-bar.php                 | 11 ++++
     src/wp-includes/class-wp-user-request.php     | 14 +++++
     src/wp-includes/user.php                      | 62 +++++++++++++------
     10 files changed, 163 insertions(+), 33 deletions(-)
     create mode 100644 src/wp-admin/network/erase-personal-data.php
     create mode 100644 src/wp-admin/network/export-personal-data.php
    
    diff --git a/src/wp-admin/erase-personal-data.php b/src/wp-admin/erase-personal-data.php
    index c96d80a9b5..716ba4580e 100644
    a b require_once ABSPATH . 'wp-admin/admin-header.php'; 
    110110
    111111        <?php settings_errors(); ?>
    112112
    113         <form action="<?php echo esc_url( admin_url( 'erase-personal-data.php' ) ); ?>" method="post" class="wp-privacy-request-form">
     113        <form action="<?php echo esc_url( self_admin_url( 'erase-personal-data.php' ) ); ?>" method="post" class="wp-privacy-request-form">
    114114                <h2><?php esc_html_e( 'Add Data Erasure Request' ); ?></h2>
    115115                <div class="wp-privacy-request-form-field">
    116116                        <table class="form-table">
  • src/wp-admin/export-personal-data.php

    diff --git a/src/wp-admin/export-personal-data.php b/src/wp-admin/export-personal-data.php
    index 64b9653c3c..6cc7a644a1 100644
    a b require_once ABSPATH . 'wp-admin/admin-header.php'; 
    110110
    111111        <?php settings_errors(); ?>
    112112
    113         <form action="<?php echo esc_url( admin_url( 'export-personal-data.php' ) ); ?>" method="post" class="wp-privacy-request-form">
     113        <form action="<?php echo esc_url( self_admin_url( 'export-personal-data.php' ) ); ?>" method="post" class="wp-privacy-request-form">
    114114                <h2><?php esc_html_e( 'Add Data Export Request' ); ?></h2>
    115115                <div class="wp-privacy-request-form-field">
    116116                <table class="form-table">
  • src/wp-admin/includes/class-wp-privacy-requests-table.php

    diff --git a/src/wp-admin/includes/class-wp-privacy-requests-table.php b/src/wp-admin/includes/class-wp-privacy-requests-table.php
    index 18a82590be..3b7b35023b 100644
    a b abstract class WP_Privacy_Requests_Table extends WP_List_Table { 
    375375                        'post_status'    => 'any',
    376376                        's'              => isset( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : '',
    377377                );
     378                if ( is_network_admin() ) {
     379                        $args['meta_query'] = array(
     380                                array(
     381                                        'key'   => '_wp_user_request_blog_id',
     382                                        'value' => 0,
     383                                ),
     384                        );
     385                }
     386                else {
     387                        $args['meta_query'] = array(
     388                                'relation' => 'OR',
     389                                array(
     390                                        'key'     => '_wp_user_request_blog_id',
     391                                        'value'   => get_current_blog_id(),
     392                                ),
     393                                // The meta key will not exist for requests submitted before 5.5.
     394                                array(
     395                                        'key'     => '_wp_user_request_blog_id',
     396                                        'compare' => 'NOT EXISTS',
     397                                ),
     398                        );
     399                }
    378400
    379401                $orderby_mapping = array(
    380402                        'requester' => 'post_title',
  • src/wp-admin/includes/privacy-tools.php

    diff --git a/src/wp-admin/includes/privacy-tools.php b/src/wp-admin/includes/privacy-tools.php
    index d5853185eb..015acf587e 100644
    a b function _wp_personal_data_handle_actions() { 
    146146                                        break;
    147147                                }
    148148
    149                                 $request_id = wp_create_user_request( $email_address, $action_type, array(), $status );
     149                                $request_id = wp_create_user_request(
     150                                        $email_address,
     151                                        $action_type,
     152                                        array(),
     153                                        is_network_admin() ? 0 : get_current_blog_id()
     154                                );
     155
    150156                                $message    = '';
    151157
    152158                                if ( is_wp_error( $request_id ) ) {
    function _wp_personal_data_cleanup_requests() { 
    196202        /** This filter is documented in wp-includes/user.php */
    197203        $expires = (int) apply_filters( 'user_request_key_expiration', DAY_IN_SECONDS );
    198204
    199         $requests_query = new WP_Query(
    200                 array(
    201                         'post_type'      => 'user_request',
    202                         'posts_per_page' => -1,
    203                         'post_status'    => 'request-pending',
    204                         'fields'         => 'ids',
    205                         'date_query'     => array(
    206                                 array(
    207                                         'column' => 'post_modified_gmt',
    208                                         'before' => $expires . ' seconds ago',
    209                                 ),
     205        $args = array(
     206                'post_type'      => 'user_request',
     207                'posts_per_page' => -1,
     208                'post_status'    => 'request-pending',
     209                'fields'         => 'ids',
     210                'date_query'     => array(
     211                        array(
     212                                'column' => 'post_modified_gmt',
     213                                'before' => $expires . ' seconds ago',
    210214                        ),
    211                 )
     215                ),
    212216        );
     217        if ( is_network_admin() ) {
     218                $args['meta_query'] = array(
     219                        array(
     220                                'key'   => '_wp_user_request_blog_id',
     221                                'value' => 0,
     222                        ),
     223                );
     224        }
     225        else {
     226                $args['meta_query'] = array(
     227                        'relation' => 'OR',
     228                        array(
     229                                'key'     => '_wp_user_request_blog_id',
     230                                'value'   => get_current_blog_id(),
     231                        ),
     232                        // The meta key will not exist for requests submitted before 5.5.
     233                        array(
     234                                'key'     => '_wp_user_request_blog_id',
     235                                'compare' => 'NOT EXISTS',
     236                        ),
     237                );
     238        }
     239
     240        $requests_query = new WP_Query( $args );
    213241
    214242        $request_ids = $requests_query->posts;
    215243
  • new file src/wp-admin/network/erase-personal-data.php

    diff --git a/src/wp-admin/network/erase-personal-data.php b/src/wp-admin/network/erase-personal-data.php
    new file mode 100644
    index 0000000000..30b1d5446b
    - +  
     1<?php
     2/**
     3 * Privacy tools, Erase Personal Data screen.
     4 *
     5 * @package WordPress
     6 * @subpackage Administration
     7 */
     8
     9/** Load WordPress Administration Bootstrap */
     10require_once __DIR__ . '/admin.php';
     11
     12require ABSPATH . 'wp-admin/erase-personal-data.php';
  • new file src/wp-admin/network/export-personal-data.php

    diff --git a/src/wp-admin/network/export-personal-data.php b/src/wp-admin/network/export-personal-data.php
    new file mode 100644
    index 0000000000..12a46152b1
    - +  
     1<?php
     2/**
     3 * Privacy tools, Export Personal Data screen.
     4 *
     5 * @package WordPress
     6 * @subpackage Administration
     7 */
     8
     9/** Load WordPress Administration Bootstrap */
     10require_once __DIR__ . '/admin.php';
     11
     12require ABSPATH . 'wp-admin/export-personal-data.php';
  • src/wp-admin/network/menu.php

    diff --git a/src/wp-admin/network/menu.php b/src/wp-admin/network/menu.php
    index ee987c83c6..83cd06e631 100644
    a b $submenu['plugins.php'][5] = array( __( 'Installed Plugins' ), 'manage_network_ 
    111111$submenu['plugins.php'][10] = array( __( 'Add Plugin' ), 'install_plugins', 'plugin-install.php' );
    112112$submenu['plugins.php'][15] = array( __( 'Plugin File Editor' ), 'edit_plugins', 'plugin-editor.php' );
    113113
     114$menu[21]                 = array( __( 'Tools' ), 'edit_posts', 'tools.php', '', 'menu-top menu-icon-tools', 'menu-tools', 'dashicons-admin-tools' );
     115$submenu['tools.php'][5]  = array( __( 'Available Tools' ), 'edit_posts', 'tools.php' );
     116$submenu['tools.php'][25] = array( __( 'Export Personal Data' ), 'export_others_personal_data', 'export-personal-data.php' );
     117$submenu['tools.php'][30] = array( __( 'Erase Personal Data' ), 'erase_others_personal_data', 'erase-personal-data.php' );
     118
    114119$menu[25] = array( __( 'Settings' ), 'manage_network_options', 'settings.php', '', 'menu-top menu-icon-settings', 'menu-settings', 'dashicons-admin-settings' );
    115120if ( defined( 'MULTISITE' ) && defined( 'WP_ALLOW_MULTISITE' ) && WP_ALLOW_MULTISITE ) {
    116121        $submenu['settings.php'][5]  = array( __( 'Network Settings' ), 'manage_network_options', 'settings.php' );
  • src/wp-includes/admin-bar.php

    diff --git a/src/wp-includes/admin-bar.php b/src/wp-includes/admin-bar.php
    index 5fe00e9801..bafacab415 100644
    a b function wp_admin_bar_my_sites_menu( $wp_admin_bar ) { 
    646646                        );
    647647                }
    648648
     649                if ( current_user_can( 'edit_posts' ) ) {
     650                        $wp_admin_bar->add_node(
     651                                array(
     652                                        'parent' => 'network-admin',
     653                                        'id'     => 'network-admin-tools',
     654                                        'title'  => __( 'Tools' ),
     655                                        'href'   => network_admin_url( 'tools.php' ),
     656                                )
     657                        );
     658                }
     659
    649660                if ( current_user_can( 'manage_network_options' ) ) {
    650661                        $wp_admin_bar->add_node(
    651662                                array(
  • src/wp-includes/class-wp-user-request.php

    diff --git a/src/wp-includes/class-wp-user-request.php b/src/wp-includes/class-wp-user-request.php
    index dc8ca7cdbd..1ca1b9c583 100644
    a b final class WP_User_Request { 
    9898         */
    9999        public $confirm_key = '';
    100100
     101        /**
     102         * Blog ID the request was submitted to.
     103         *
     104         * Will be 0 if the request was submitted at the network-level.
     105         *
     106         * @since 5.5
     107         * @var int
     108         */
     109        public $blog_id;
     110
    101111        /**
    102112         * Constructor.
    103113         *
    final class WP_User_Request { 
    117127                $this->completed_timestamp = (int) get_post_meta( $post->ID, '_wp_user_request_completed_timestamp', true );
    118128                $this->request_data        = json_decode( $post->post_content, true );
    119129                $this->confirm_key         = $post->post_password;
     130
     131                $blog_id                   = get_post_meta( $post->ID, '_wp_user_request_blog_id', true );
     132                // $blog_id will be the empty string for requests submitted prior to 5.5.
     133                $this->blog_id             = '' !== $blog_id ? (int) $blog_id : get_current_blog_id();
    120134        }
    121135}
  • src/wp-includes/user.php

    diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php
    index ecd22ab38c..8c58139452 100644
    a b function _wp_privacy_account_request_confirmed_message( $request_id ) { 
    46984698 * users on the site, or guests without a user account.
    46994699 *
    47004700 * @since 4.9.6
     4701 *
    47014702 * @since 5.7.0 Added the `$status` parameter.
     4703 * @since x.y.z Added `$blog_id` parameter.
     4704
    47024705 *
    4703  * @param string $email_address           User email address. This can be the address of a registered
    4704  *                                        or non-registered user.
    4705  * @param string $action_name             Name of the action that is being confirmed. Required.
    4706  * @param array  $request_data            Misc data you want to send with the verification request and pass
    4707  *                                        to the actions once the request is confirmed.
    4708  * @param string $status                  Optional request status (pending or confirmed). Default 'pending'.
    4709  * @return int|WP_Error                   Returns the request ID if successful, or a WP_Error object on failure.
     4706 * @param string $email_address User email address. This can be the address of a registered
     4707 *                              or non-registered user.
     4708 * @param string $action_name   Name of the action that is being confirmed. Required.
     4709 * @param array  $request_data  Misc data you want to send with the verification request and pass
     4710 *                              to the actions once the request is confirmed.
     4711 * @param string $status        Optional request status (pending or confirmed). Default 'pending'.
     4712 * @param int|null $blog_id     Blog ID to create request in.  Use 0 for a network-level requests.  Default is null, indicating the current blog ID.
     4713 * @return int|WP_Error         Returns the request ID if successful, or a WP_Error object on failure.
    47104714 */
    4711 function wp_create_user_request( $email_address = '', $action_name = '', $request_data = array(), $status = 'pending' ) {
     4715function wp_create_user_request( $email_address = '', $action_name = '', $request_data = array(), $status = 'pending', $blog_id = null ) {
    47124716        $email_address = sanitize_email( $email_address );
    47134717        $action_name   = sanitize_key( $action_name );
     4718        $blog_id       = null === $blog_id ? get_current_blog_id() : (int) $blog_id;
    47144719
    47154720        if ( ! is_email( $email_address ) ) {
    47164721                return new WP_Error( 'invalid_email', __( 'Invalid email address.' ) );
    function wp_create_user_request( $email_address = '', $action_name = '', $reques 
    47244729                return new WP_Error( 'invalid_status', __( 'Invalid request status.' ) );
    47254730        }
    47264731
     4732        if ( $blog_id && ! get_site( $blog_id ) ) {
     4733                return new WP_Error( 'invalid_blog_id', __( 'Invalid blog ID.' ) );
     4734        }
     4735
    47274736        $user    = get_user_by( 'email', $email_address );
    47284737        $user_id = $user && ! is_wp_error( $user ) ? $user->ID : 0;
    47294738
    47304739        // Check for duplicates.
    4731         $requests_query = new WP_Query(
    4732                 array(
    4733                         'post_type'     => 'user_request',
    4734                         'post_name__in' => array( $action_name ), // Action name stored in post_name column.
    4735                         'title'         => $email_address,        // Email address stored in post_title column.
    4736                         'post_status'   => array(
    4737                                 'request-pending',
    4738                                 'request-confirmed',
     4740        $args = array(
     4741                'post_type'     => 'user_request',
     4742                'post_name__in' => array( $action_name ), // Action name stored in post_name column.
     4743                'title'         => $email_address,        // Email address stored in post_title column.
     4744                'post_status'   => array(
     4745                        'request-pending',
     4746                        'request-confirmed',
     4747                ),
     4748                'fields'        => 'ids',
     4749                'meta_query'    => array(
     4750                        array(
     4751                                'key'   => '_wp_user_request_blog_id',
     4752                                'value' => $blog_id,
    47394753                        ),
    4740                         'fields'        => 'ids',
    4741                 )
     4754                ),
    47424755        );
     4756        if ( $blog_id ) {
     4757                // add a check for requests submitted prior to 5.5.
     4758                $args['meta_query']['relation'] = 'OR';
     4759                $args['meta_query'][] = array(
     4760                        'key'     => '_wp_user_request_blog_id',
     4761                        'compare' => 'NOT EXISTS',
     4762                );
     4763        }
     4764
     4765        $requests_query = new WP_Query( $args );
    47434766
    47444767        if ( $requests_query->found_posts ) {
    47454768                return new WP_Error( 'duplicate_request', __( 'An incomplete personal data request for this email address already exists.' ) );
    function wp_create_user_request( $email_address = '', $action_name = '', $reques 
    47554778                        'post_type'     => 'user_request',
    47564779                        'post_date'     => current_time( 'mysql', false ),
    47574780                        'post_date_gmt' => current_time( 'mysql', true ),
     4781                        'meta_input'    => array(
     4782                                '_wp_user_request_blog_id' => $blog_id,
     4783                        ),
    47584784                ),
    47594785                true
    47604786        );
  • new file src/wp-admin/network/tools.php

    -- 
    2.37.0.windows.1
    
    From 774d4585663ef9049b4ef6477a4c833d2196ae84 Mon Sep 17 00:00:00 2001
    From: Paul Biron <paul@sparrowhawkcomputing.com>
    Date: Sun, 29 Jun 2025 12:03:15 -0600
    Subject: [PATCH] Add an empty network/tools.php screen.
    
    ---
     src/wp-admin/network/tools.php | 58 ++++++++++++++++++++++++++++++++++
     1 file changed, 58 insertions(+)
     create mode 100644 src/wp-admin/network/tools.php
    
    diff --git a/src/wp-admin/network/tools.php b/src/wp-admin/network/tools.php
    new file mode 100644
    index 0000000000..911c61ff2c
    - +  
     1<?php
     2/**
     3 * Multisite Tools Administration Screen.
     4 *
     5 * @package WordPress
     6 * @subpackage Administration
     7 */
     8
     9if ( isset( $_GET['page'] ) && ! empty( $_POST ) ) {
     10        // Ensure POST-ing to `tools.php?page=export_personal_data` and `tools.php?page=remove_personal_data`
     11        // continues to work after creating the new files for exporting and erasing of personal data.
     12        if ( 'export_personal_data' === $_GET['page'] ) {
     13                require_once ABSPATH . 'wp-admin/network/export-personal-data.php';
     14                return;
     15        } elseif ( 'remove_personal_data' === $_GET['page'] ) {
     16                require_once ABSPATH . 'wp-admin/network/erase-personal-data.php';
     17                return;
     18        }
     19}
     20
     21if ( isset( $_GET['page'] ) ) {
     22        // These were also moved to files in WP 5.3.
     23        if ( 'export_personal_data' === $_GET['page'] ) {
     24                require_once dirname( __DIR__ ) . '/wp-load.php';
     25                wp_redirect( admin_url( 'network/export-personal-data.php' ), 301 );
     26                exit;
     27        } elseif ( 'remove_personal_data' === $_GET['page'] ) {
     28                require_once dirname( __DIR__ ) . '/wp-load.php';
     29                wp_redirect( admin_url( 'network/erase-personal-data.php' ), 301 );
     30                exit;
     31        }
     32}
     33
     34/** WordPress Administration Bootstrap */
     35require_once __DIR__ . '/admin.php';
     36
     37// Used in the HTML title tag.
     38$title = __( 'Tools' );
     39
     40require_once ABSPATH . 'wp-admin/admin-header.php';
     41
     42?>
     43<div class="wrap">
     44<h1><?php echo esc_html( $title ); ?></h1>
     45<?php
     46
     47/**
     48 * Fires at the end of the Tools Administration screen.
     49 *
     50 * @since x.y.z
     51 */
     52do_action( 'network_tool_box' );
     53
     54?>
     55</div>
     56<?php
     57
     58require_once ABSPATH . 'wp-admin/admin-footer.php';
  • src/wp-admin/includes/privacy-tools.php

    -- 
    2.37.0.windows.1
    
    From 2981b870b052c5bed48dde535db37e2e6781d675 Mon Sep 17 00:00:00 2001
    From: Paul Biron <paul@sparrowhawkcomputing.com>
    Date: Wed, 2 Jul 2025 11:53:56 -0600
    Subject: [PATCH] Bug fix: in wp_personal_data_handle_actions(), pass $status
     as the 4th param to wp_create_user_request().
    
    This was accidentally left out when refreshing the patch the other day.
    ---
     src/wp-admin/includes/privacy-tools.php | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/src/wp-admin/includes/privacy-tools.php b/src/wp-admin/includes/privacy-tools.php
    index 015acf587e..27844a3d9b 100644
    a b function _wp_personal_data_handle_actions() { 
    150150                                        $email_address,
    151151                                        $action_type,
    152152                                        array(),
     153                                        $status,
    153154                                        is_network_admin() ? 0 : get_current_blog_id()
    154155                                );
    155156