| 1 | <?php |
|---|
| 2 | /** |
|---|
| 3 | * WordPress user administration API. |
|---|
| 4 | * |
|---|
| 5 | * @package WordPress |
|---|
| 6 | * @subpackage Administration |
|---|
| 7 | */ |
|---|
| 8 | |
|---|
| 9 | /** |
|---|
| 10 | * Creates a new user from the "Users" form using $_POST information. |
|---|
| 11 | * |
|---|
| 12 | * @since 2.0.0 |
|---|
| 13 | * |
|---|
| 14 | * @return int|WP_Error WP_Error or User ID. |
|---|
| 15 | */ |
|---|
| 16 | function add_user() { |
|---|
| 17 | return edit_user(); |
|---|
| 18 | } |
|---|
| 19 | |
|---|
| 20 | /** |
|---|
| 21 | * Edit user settings based on contents of $_POST |
|---|
| 22 | * |
|---|
| 23 | * Used on user-edit.php and profile.php to manage and process user options, passwords etc. |
|---|
| 24 | * |
|---|
| 25 | * @since 2.0.0 |
|---|
| 26 | * |
|---|
| 27 | * @param int $user_id Optional. User ID. |
|---|
| 28 | * @return int|WP_Error user id of the updated user |
|---|
| 29 | */ |
|---|
| 30 | function edit_user( $user_id = 0 ) { |
|---|
| 31 | $wp_roles = wp_roles(); |
|---|
| 32 | $user = new stdClass; |
|---|
| 33 | if ( $user_id ) { |
|---|
| 34 | $update = true; |
|---|
| 35 | $user->ID = (int) $user_id; |
|---|
| 36 | $userdata = get_userdata( $user_id ); |
|---|
| 37 | $user->user_login = wp_slash( $userdata->user_login ); |
|---|
| 38 | } else { |
|---|
| 39 | $update = false; |
|---|
| 40 | } |
|---|
| 41 | |
|---|
| 42 | if ( !$update && isset( $_POST['user_login'] ) ) |
|---|
| 43 | $user->user_login = sanitize_user($_POST['user_login'], true); |
|---|
| 44 | |
|---|
| 45 | $pass1 = $pass2 = ''; |
|---|
| 46 | if ( isset( $_POST['pass1'] ) ) |
|---|
| 47 | $pass1 = $_POST['pass1']; |
|---|
| 48 | if ( isset( $_POST['pass2'] ) ) |
|---|
| 49 | $pass2 = $_POST['pass2']; |
|---|
| 50 | |
|---|
| 51 | if ( isset( $_POST['role'] ) && current_user_can( 'edit_users' ) ) { |
|---|
| 52 | $new_role = sanitize_text_field( $_POST['role'] ); |
|---|
| 53 | $potential_role = isset($wp_roles->role_objects[$new_role]) ? $wp_roles->role_objects[$new_role] : false; |
|---|
| 54 | // Don't let anyone with 'edit_users' (admins) edit their own role to something without it. |
|---|
| 55 | // Multisite super admins can freely edit their blog roles -- they possess all caps. |
|---|
| 56 | if ( ( is_multisite() && current_user_can( 'manage_sites' ) ) || $user_id != get_current_user_id() || ($potential_role && $potential_role->has_cap( 'edit_users' ) ) ) |
|---|
| 57 | $user->role = $new_role; |
|---|
| 58 | |
|---|
| 59 | // If the new role isn't editable by the logged-in user die with error |
|---|
| 60 | $editable_roles = get_editable_roles(); |
|---|
| 61 | if ( ! empty( $new_role ) && empty( $editable_roles[$new_role] ) ) |
|---|
| 62 | wp_die( __( 'Sorry, you are not allowed to give users that role.' ), 403 ); |
|---|
| 63 | } |
|---|
| 64 | |
|---|
| 65 | if ( isset( $_POST['email'] )) |
|---|
| 66 | $user->user_email = sanitize_text_field( wp_unslash( $_POST['email'] ) ); |
|---|
| 67 | if ( isset( $_POST['url'] ) ) { |
|---|
| 68 | if ( empty ( $_POST['url'] ) || $_POST['url'] == 'http://' ) { |
|---|
| 69 | $user->user_url = ''; |
|---|
| 70 | } else { |
|---|
| 71 | $user->user_url = esc_url_raw( $_POST['url'] ); |
|---|
| 72 | $protocols = implode( '|', array_map( 'preg_quote', wp_allowed_protocols() ) ); |
|---|
| 73 | $user->user_url = preg_match('/^(' . $protocols . '):/is', $user->user_url) ? $user->user_url : 'http://'.$user->user_url; |
|---|
| 74 | } |
|---|
| 75 | } |
|---|
| 76 | if ( isset( $_POST['first_name'] ) ) |
|---|
| 77 | $user->first_name = sanitize_text_field( $_POST['first_name'] ); |
|---|
| 78 | if ( isset( $_POST['last_name'] ) ) |
|---|
| 79 | $user->last_name = sanitize_text_field( $_POST['last_name'] ); |
|---|
| 80 | if ( isset( $_POST['nickname'] ) ) |
|---|
| 81 | $user->nickname = sanitize_text_field( $_POST['nickname'] ); |
|---|
| 82 | if ( isset( $_POST['display_name'] ) ) |
|---|
| 83 | $user->display_name = sanitize_text_field( $_POST['display_name'] ); |
|---|
| 84 | |
|---|
| 85 | if ( isset( $_POST['description'] ) ) |
|---|
| 86 | $user->description = trim( $_POST['description'] ); |
|---|
| 87 | |
|---|
| 88 | foreach ( wp_get_user_contact_methods( $user ) as $method => $name ) { |
|---|
| 89 | if ( isset( $_POST[$method] )) |
|---|
| 90 | $user->$method = sanitize_text_field( $_POST[$method] ); |
|---|
| 91 | } |
|---|
| 92 | |
|---|
| 93 | if ( $update ) { |
|---|
| 94 | $user->rich_editing = isset( $_POST['rich_editing'] ) && 'false' === $_POST['rich_editing'] ? 'false' : 'true'; |
|---|
| 95 | $user->syntax_highlighting = isset( $_POST['syntax_highlighting'] ) && 'false' === $_POST['syntax_highlighting'] ? 'false' : 'true'; |
|---|
| 96 | $user->admin_color = isset( $_POST['admin_color'] ) ? sanitize_text_field( $_POST['admin_color'] ) : 'fresh'; |
|---|
| 97 | $user->show_admin_bar_front = isset( $_POST['admin_bar_front'] ) ? 'true' : 'false'; |
|---|
| 98 | $user->locale = ''; |
|---|
| 99 | |
|---|
| 100 | if ( isset( $_POST['locale'] ) ) { |
|---|
| 101 | $locale = sanitize_text_field( $_POST['locale'] ); |
|---|
| 102 | if ( 'site-default' === $locale ) { |
|---|
| 103 | $locale = ''; |
|---|
| 104 | } elseif ( '' === $locale ) { |
|---|
| 105 | $locale = 'en_US'; |
|---|
| 106 | } elseif ( ! in_array( $locale, get_available_languages(), true ) ) { |
|---|
| 107 | $locale = ''; |
|---|
| 108 | } |
|---|
| 109 | |
|---|
| 110 | $user->locale = $locale; |
|---|
| 111 | } |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | $user->comment_shortcuts = isset( $_POST['comment_shortcuts'] ) && 'true' == $_POST['comment_shortcuts'] ? 'true' : ''; |
|---|
| 115 | |
|---|
| 116 | $user->use_ssl = 0; |
|---|
| 117 | if ( !empty($_POST['use_ssl']) ) |
|---|
| 118 | $user->use_ssl = 1; |
|---|
| 119 | |
|---|
| 120 | $errors = new WP_Error(); |
|---|
| 121 | |
|---|
| 122 | /* checking that username has been typed */ |
|---|
| 123 | if ( $user->user_login == '' ) |
|---|
| 124 | $errors->add( 'user_login', __( '<strong>ERROR</strong>: Please enter a username.' ) ); |
|---|
| 125 | |
|---|
| 126 | /* checking that nickname has been typed */ |
|---|
| 127 | if ( $update && empty( $user->nickname ) ) { |
|---|
| 128 | $errors->add( 'nickname', __( '<strong>ERROR</strong>: Please enter a nickname.' ) ); |
|---|
| 129 | } |
|---|
| 130 | |
|---|
| 131 | /** |
|---|
| 132 | * Fires before the password and confirm password fields are checked for congruity. |
|---|
| 133 | * |
|---|
| 134 | * @since 1.5.1 |
|---|
| 135 | * |
|---|
| 136 | * @param string $user_login The username. |
|---|
| 137 | * @param string $pass1 The password (passed by reference). |
|---|
| 138 | * @param string $pass2 The confirmed password (passed by reference). |
|---|
| 139 | */ |
|---|
| 140 | do_action_ref_array( 'check_passwords', array( $user->user_login, &$pass1, &$pass2 ) ); |
|---|
| 141 | |
|---|
| 142 | // Check for blank password when adding a user. |
|---|
| 143 | if ( ! $update && empty( $pass1 ) ) { |
|---|
| 144 | $errors->add( 'pass', __( '<strong>ERROR</strong>: Please enter a password.' ), array( 'form-field' => 'pass1' ) ); |
|---|
| 145 | } |
|---|
| 146 | |
|---|
| 147 | // Check for "\" in password. |
|---|
| 148 | if ( false !== strpos( wp_unslash( $pass1 ), "\\" ) ) { |
|---|
| 149 | $errors->add( 'pass', __( '<strong>ERROR</strong>: Passwords may not contain the character "\\".' ), array( 'form-field' => 'pass1' ) ); |
|---|
| 150 | } |
|---|
| 151 | |
|---|
| 152 | // Checking the password has been typed twice the same. |
|---|
| 153 | if ( ( $update || ! empty( $pass1 ) ) && $pass1 != $pass2 ) { |
|---|
| 154 | $errors->add( 'pass', __( '<strong>ERROR</strong>: Please enter the same password in both password fields.' ), array( 'form-field' => 'pass1' ) ); |
|---|
| 155 | } |
|---|
| 156 | |
|---|
| 157 | if ( !empty( $pass1 ) ) |
|---|
| 158 | $user->user_pass = $pass1; |
|---|
| 159 | |
|---|
| 160 | if ( !$update && isset( $_POST['user_login'] ) && !validate_username( $_POST['user_login'] ) ) |
|---|
| 161 | $errors->add( 'user_login', __( '<strong>ERROR</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.' )); |
|---|
| 162 | |
|---|
| 163 | if ( !$update && username_exists( $user->user_login ) ) |
|---|
| 164 | $errors->add( 'user_login', __( '<strong>ERROR</strong>: This username is already registered. Please choose another one.' )); |
|---|
| 165 | |
|---|
| 166 | /** This filter is documented in wp-includes/user.php */ |
|---|
| 167 | $illegal_logins = (array) apply_filters( 'illegal_user_logins', array() ); |
|---|
| 168 | |
|---|
| 169 | if ( in_array( strtolower( $user->user_login ), array_map( 'strtolower', $illegal_logins ) ) ) { |
|---|
| 170 | $errors->add( 'invalid_username', __( '<strong>ERROR</strong>: Sorry, that username is not allowed.' ) ); |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | /* checking email address */ |
|---|
| 174 | if ( empty( $user->user_email ) ) { |
|---|
| 175 | $errors->add( 'empty_email', __( '<strong>ERROR</strong>: Please enter an email address.' ), array( 'form-field' => 'email' ) ); |
|---|
| 176 | } elseif ( !is_email( $user->user_email ) ) { |
|---|
| 177 | $errors->add( 'invalid_email', __( '<strong>ERROR</strong>: The email address isn’t correct.' ), array( 'form-field' => 'email' ) ); |
|---|
| 178 | } elseif ( ( $owner_id = email_exists($user->user_email) ) && ( !$update || ( $owner_id != $user->ID ) ) ) { |
|---|
| 179 | $errors->add( 'email_exists', __('<strong>ERROR</strong>: This email is already registered, please choose another one.'), array( 'form-field' => 'email' ) ); |
|---|
| 180 | } |
|---|
| 181 | |
|---|
| 182 | /** |
|---|
| 183 | * Fires before user profile update errors are returned. |
|---|
| 184 | * |
|---|
| 185 | * @since 2.8.0 |
|---|
| 186 | * |
|---|
| 187 | * @param WP_Error $errors WP_Error object (passed by reference). |
|---|
| 188 | * @param bool $update Whether this is a user update. |
|---|
| 189 | * @param stdClass $user User object (passed by reference). |
|---|
| 190 | */ |
|---|
| 191 | do_action_ref_array( 'user_profile_update_errors', array( &$errors, $update, &$user ) ); |
|---|
| 192 | |
|---|
| 193 | if ( $errors->get_error_codes() ) |
|---|
| 194 | return $errors; |
|---|
| 195 | |
|---|
| 196 | if ( $update ) { |
|---|
| 197 | $user_id = wp_update_user( $user ); |
|---|
| 198 | } else { |
|---|
| 199 | $user_id = wp_insert_user( $user ); |
|---|
| 200 | $notify = isset( $_POST['send_user_notification'] ) ? 'both' : 'admin'; |
|---|
| 201 | |
|---|
| 202 | /** |
|---|
| 203 | * Fires after a new user has been created. |
|---|
| 204 | * |
|---|
| 205 | * @since 4.4.0 |
|---|
| 206 | * |
|---|
| 207 | * @param int $user_id ID of the newly created user. |
|---|
| 208 | * @param string $notify Type of notification that should happen. See wp_send_new_user_notifications() |
|---|
| 209 | * for more information on possible values. |
|---|
| 210 | */ |
|---|
| 211 | do_action( 'edit_user_created_user', $user_id, $notify ); |
|---|
| 212 | } |
|---|
| 213 | return $user_id; |
|---|
| 214 | } |
|---|
| 215 | |
|---|
| 216 | /** |
|---|
| 217 | * Fetch a filtered list of user roles that the current user is |
|---|
| 218 | * allowed to edit. |
|---|
| 219 | * |
|---|
| 220 | * Simple function who's main purpose is to allow filtering of the |
|---|
| 221 | * list of roles in the $wp_roles object so that plugins can remove |
|---|
| 222 | * inappropriate ones depending on the situation or user making edits. |
|---|
| 223 | * Specifically because without filtering anyone with the edit_users |
|---|
| 224 | * capability can edit others to be administrators, even if they are |
|---|
| 225 | * only editors or authors. This filter allows admins to delegate |
|---|
| 226 | * user management. |
|---|
| 227 | * |
|---|
| 228 | * @since 2.8.0 |
|---|
| 229 | * |
|---|
| 230 | * @return array |
|---|
| 231 | */ |
|---|
| 232 | function get_editable_roles() { |
|---|
| 233 | $all_roles = wp_roles()->roles; |
|---|
| 234 | |
|---|
| 235 | /** |
|---|
| 236 | * Filters the list of editable roles. |
|---|
| 237 | * |
|---|
| 238 | * @since 2.8.0 |
|---|
| 239 | * |
|---|
| 240 | * @param array $all_roles List of roles. |
|---|
| 241 | */ |
|---|
| 242 | $editable_roles = apply_filters( 'editable_roles', $all_roles ); |
|---|
| 243 | |
|---|
| 244 | return $editable_roles; |
|---|
| 245 | } |
|---|
| 246 | |
|---|
| 247 | /** |
|---|
| 248 | * Retrieve user data and filter it. |
|---|
| 249 | * |
|---|
| 250 | * @since 2.0.5 |
|---|
| 251 | * |
|---|
| 252 | * @param int $user_id User ID. |
|---|
| 253 | * @return WP_User|bool WP_User object on success, false on failure. |
|---|
| 254 | */ |
|---|
| 255 | function get_user_to_edit( $user_id ) { |
|---|
| 256 | $user = get_userdata( $user_id ); |
|---|
| 257 | |
|---|
| 258 | if ( $user ) |
|---|
| 259 | $user->filter = 'edit'; |
|---|
| 260 | |
|---|
| 261 | return $user; |
|---|
| 262 | } |
|---|
| 263 | |
|---|
| 264 | /** |
|---|
| 265 | * Retrieve the user's drafts. |
|---|
| 266 | * |
|---|
| 267 | * @since 2.0.0 |
|---|
| 268 | * |
|---|
| 269 | * @global wpdb $wpdb WordPress database abstraction object. |
|---|
| 270 | * |
|---|
| 271 | * @param int $user_id User ID. |
|---|
| 272 | * @return array |
|---|
| 273 | */ |
|---|
| 274 | function get_users_drafts( $user_id ) { |
|---|
| 275 | global $wpdb; |
|---|
| 276 | $query = $wpdb->prepare("SELECT ID, post_title FROM $wpdb->posts WHERE post_type = 'post' AND post_status = 'draft' AND post_author = %d ORDER BY post_modified DESC", $user_id); |
|---|
| 277 | |
|---|
| 278 | /** |
|---|
| 279 | * Filters the user's drafts query string. |
|---|
| 280 | * |
|---|
| 281 | * @since 2.0.0 |
|---|
| 282 | * |
|---|
| 283 | * @param string $query The user's drafts query string. |
|---|
| 284 | */ |
|---|
| 285 | $query = apply_filters( 'get_users_drafts', $query ); |
|---|
| 286 | return $wpdb->get_results( $query ); |
|---|
| 287 | } |
|---|
| 288 | |
|---|
| 289 | /** |
|---|
| 290 | * Remove user and optionally reassign posts and links to another user. |
|---|
| 291 | * |
|---|
| 292 | * If the $reassign parameter is not assigned to a User ID, then all posts will |
|---|
| 293 | * be deleted of that user. The action {@see 'delete_user'} that is passed the User ID |
|---|
| 294 | * being deleted will be run after the posts are either reassigned or deleted. |
|---|
| 295 | * The user meta will also be deleted that are for that User ID. |
|---|
| 296 | * |
|---|
| 297 | * @since 2.0.0 |
|---|
| 298 | * |
|---|
| 299 | * @global wpdb $wpdb WordPress database abstraction object. |
|---|
| 300 | * |
|---|
| 301 | * @param int $id User ID. |
|---|
| 302 | * @param int $reassign Optional. Reassign posts and links to new User ID. |
|---|
| 303 | * @return bool True when finished. |
|---|
| 304 | */ |
|---|
| 305 | function wp_delete_user( $id, $reassign = null ) { |
|---|
| 306 | global $wpdb; |
|---|
| 307 | |
|---|
| 308 | if ( ! is_numeric( $id ) ) { |
|---|
| 309 | return false; |
|---|
| 310 | } |
|---|
| 311 | |
|---|
| 312 | $id = (int) $id; |
|---|
| 313 | $user = new WP_User( $id ); |
|---|
| 314 | |
|---|
| 315 | if ( !$user->exists() ) |
|---|
| 316 | return false; |
|---|
| 317 | |
|---|
| 318 | // Normalize $reassign to null or a user ID. 'novalue' was an older default. |
|---|
| 319 | if ( 'novalue' === $reassign ) { |
|---|
| 320 | $reassign = null; |
|---|
| 321 | } elseif ( null !== $reassign ) { |
|---|
| 322 | $reassign = (int) $reassign; |
|---|
| 323 | } |
|---|
| 324 | |
|---|
| 325 | /** |
|---|
| 326 | * Fires immediately before a user is deleted from the database. |
|---|
| 327 | * |
|---|
| 328 | * @since 2.0.0 |
|---|
| 329 | * |
|---|
| 330 | * @param int $id ID of the user to delete. |
|---|
| 331 | * @param int|null $reassign ID of the user to reassign posts and links to. |
|---|
| 332 | * Default null, for no reassignment. |
|---|
| 333 | */ |
|---|
| 334 | do_action( 'delete_user', $id, $reassign ); |
|---|
| 335 | |
|---|
| 336 | if ( null === $reassign ) { |
|---|
| 337 | $post_types_to_delete = array(); |
|---|
| 338 | foreach ( get_post_types( array(), 'objects' ) as $post_type ) { |
|---|
| 339 | if ( $post_type->delete_with_user ) { |
|---|
| 340 | $post_types_to_delete[] = $post_type->name; |
|---|
| 341 | } elseif ( null === $post_type->delete_with_user && post_type_supports( $post_type->name, 'author' ) ) { |
|---|
| 342 | $post_types_to_delete[] = $post_type->name; |
|---|
| 343 | } |
|---|
| 344 | } |
|---|
| 345 | |
|---|
| 346 | /** |
|---|
| 347 | * Filters the list of post types to delete with a user. |
|---|
| 348 | * |
|---|
| 349 | * @since 3.4.0 |
|---|
| 350 | * |
|---|
| 351 | * @param array $post_types_to_delete Post types to delete. |
|---|
| 352 | * @param int $id User ID. |
|---|
| 353 | */ |
|---|
| 354 | $post_types_to_delete = apply_filters( 'post_types_to_delete_with_user', $post_types_to_delete, $id ); |
|---|
| 355 | $post_types_to_delete = implode( "', '", $post_types_to_delete ); |
|---|
| 356 | $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d AND post_type IN ('$post_types_to_delete')", $id ) ); |
|---|
| 357 | if ( $post_ids ) { |
|---|
| 358 | foreach ( $post_ids as $post_id ) |
|---|
| 359 | wp_delete_post( $post_id ); |
|---|
| 360 | } |
|---|
| 361 | |
|---|
| 362 | // Clean links |
|---|
| 363 | $link_ids = $wpdb->get_col( $wpdb->prepare("SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $id) ); |
|---|
| 364 | |
|---|
| 365 | if ( $link_ids ) { |
|---|
| 366 | foreach ( $link_ids as $link_id ) |
|---|
| 367 | wp_delete_link($link_id); |
|---|
| 368 | } |
|---|
| 369 | } else { |
|---|
| 370 | $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d", $id ) ); |
|---|
| 371 | $wpdb->update( $wpdb->posts, array('post_author' => $reassign), array('post_author' => $id) ); |
|---|
| 372 | if ( ! empty( $post_ids ) ) { |
|---|
| 373 | foreach ( $post_ids as $post_id ) |
|---|
| 374 | clean_post_cache( $post_id ); |
|---|
| 375 | } |
|---|
| 376 | $link_ids = $wpdb->get_col( $wpdb->prepare("SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $id) ); |
|---|
| 377 | $wpdb->update( $wpdb->links, array('link_owner' => $reassign), array('link_owner' => $id) ); |
|---|
| 378 | if ( ! empty( $link_ids ) ) { |
|---|
| 379 | foreach ( $link_ids as $link_id ) |
|---|
| 380 | clean_bookmark_cache( $link_id ); |
|---|
| 381 | } |
|---|
| 382 | } |
|---|
| 383 | |
|---|
| 384 | // FINALLY, delete user |
|---|
| 385 | if ( is_multisite() ) { |
|---|
| 386 | remove_user_from_blog( $id, get_current_blog_id() ); |
|---|
| 387 | } else { |
|---|
| 388 | $meta = $wpdb->get_col( $wpdb->prepare( "SELECT umeta_id FROM $wpdb->usermeta WHERE user_id = %d", $id ) ); |
|---|
| 389 | foreach ( $meta as $mid ) |
|---|
| 390 | delete_metadata_by_mid( 'user', $mid ); |
|---|
| 391 | |
|---|
| 392 | $wpdb->delete( $wpdb->users, array( 'ID' => $id ) ); |
|---|
| 393 | } |
|---|
| 394 | |
|---|
| 395 | clean_user_cache( $user ); |
|---|
| 396 | |
|---|
| 397 | /** |
|---|
| 398 | * Fires immediately after a user is deleted from the database. |
|---|
| 399 | * |
|---|
| 400 | * @since 2.9.0 |
|---|
| 401 | * |
|---|
| 402 | * @param int $id ID of the deleted user. |
|---|
| 403 | * @param int|null $reassign ID of the user to reassign posts and links to. |
|---|
| 404 | * Default null, for no reassignment. |
|---|
| 405 | */ |
|---|
| 406 | do_action( 'deleted_user', $id, $reassign ); |
|---|
| 407 | |
|---|
| 408 | return true; |
|---|
| 409 | } |
|---|
| 410 | |
|---|
| 411 | /** |
|---|
| 412 | * Remove all capabilities from user. |
|---|
| 413 | * |
|---|
| 414 | * @since 2.1.0 |
|---|
| 415 | * |
|---|
| 416 | * @param int $id User ID. |
|---|
| 417 | */ |
|---|
| 418 | function wp_revoke_user($id) { |
|---|
| 419 | $id = (int) $id; |
|---|
| 420 | |
|---|
| 421 | $user = new WP_User($id); |
|---|
| 422 | $user->remove_all_caps(); |
|---|
| 423 | } |
|---|
| 424 | |
|---|
| 425 | /** |
|---|
| 426 | * @since 2.8.0 |
|---|
| 427 | * |
|---|
| 428 | * @global int $user_ID |
|---|
| 429 | * |
|---|
| 430 | * @param false $errors Deprecated. |
|---|
| 431 | */ |
|---|
| 432 | function default_password_nag_handler($errors = false) { |
|---|
| 433 | global $user_ID; |
|---|
| 434 | // Short-circuit it. |
|---|
| 435 | if ( ! get_user_option('default_password_nag') ) |
|---|
| 436 | return; |
|---|
| 437 | |
|---|
| 438 | // get_user_setting = JS saved UI setting. else no-js-fallback code. |
|---|
| 439 | if ( 'hide' == get_user_setting('default_password_nag') || isset($_GET['default_password_nag']) && '0' == $_GET['default_password_nag'] ) { |
|---|
| 440 | delete_user_setting('default_password_nag'); |
|---|
| 441 | update_user_option($user_ID, 'default_password_nag', false, true); |
|---|
| 442 | } |
|---|
| 443 | } |
|---|
| 444 | |
|---|
| 445 | /** |
|---|
| 446 | * @since 2.8.0 |
|---|
| 447 | * |
|---|
| 448 | * @param int $user_ID |
|---|
| 449 | * @param object $old_data |
|---|
| 450 | */ |
|---|
| 451 | function default_password_nag_edit_user($user_ID, $old_data) { |
|---|
| 452 | // Short-circuit it. |
|---|
| 453 | if ( ! get_user_option('default_password_nag', $user_ID) ) |
|---|
| 454 | return; |
|---|
| 455 | |
|---|
| 456 | $new_data = get_userdata($user_ID); |
|---|
| 457 | |
|---|
| 458 | // Remove the nag if the password has been changed. |
|---|
| 459 | if ( $new_data->user_pass != $old_data->user_pass ) { |
|---|
| 460 | delete_user_setting('default_password_nag'); |
|---|
| 461 | update_user_option($user_ID, 'default_password_nag', false, true); |
|---|
| 462 | } |
|---|
| 463 | } |
|---|
| 464 | |
|---|
| 465 | /** |
|---|
| 466 | * @since 2.8.0 |
|---|
| 467 | * |
|---|
| 468 | * @global string $pagenow |
|---|
| 469 | */ |
|---|
| 470 | function default_password_nag() { |
|---|
| 471 | global $pagenow; |
|---|
| 472 | // Short-circuit it. |
|---|
| 473 | if ( 'profile.php' == $pagenow || ! get_user_option('default_password_nag') ) |
|---|
| 474 | return; |
|---|
| 475 | |
|---|
| 476 | echo '<div class="error default-password-nag">'; |
|---|
| 477 | echo '<p>'; |
|---|
| 478 | echo '<strong>' . __('Notice:') . '</strong> '; |
|---|
| 479 | _e('You’re using the auto-generated password for your account. Would you like to change it?'); |
|---|
| 480 | echo '</p><p>'; |
|---|
| 481 | printf( '<a href="%s">' . __('Yes, take me to my profile page') . '</a> | ', get_edit_profile_url() . '#password' ); |
|---|
| 482 | printf( '<a href="%s" id="default-password-nag-no">' . __('No thanks, do not remind me again') . '</a>', '?default_password_nag=0' ); |
|---|
| 483 | echo '</p></div>'; |
|---|
| 484 | } |
|---|
| 485 | |
|---|
| 486 | /** |
|---|
| 487 | * @since 3.5.0 |
|---|
| 488 | * @access private |
|---|
| 489 | */ |
|---|
| 490 | function delete_users_add_js() { ?> |
|---|
| 491 | <script> |
|---|
| 492 | jQuery(document).ready( function($) { |
|---|
| 493 | var submit = $('#submit').prop('disabled', true); |
|---|
| 494 | $('input[name="delete_option"]').one('change', function() { |
|---|
| 495 | submit.prop('disabled', false); |
|---|
| 496 | }); |
|---|
| 497 | $('#reassign_user').focus( function() { |
|---|
| 498 | $('#delete_option1').prop('checked', true).trigger('change'); |
|---|
| 499 | }); |
|---|
| 500 | }); |
|---|
| 501 | </script> |
|---|
| 502 | <?php |
|---|
| 503 | } |
|---|
| 504 | |
|---|
| 505 | /** |
|---|
| 506 | * Optional SSL preference that can be turned on by hooking to the 'personal_options' action. |
|---|
| 507 | * |
|---|
| 508 | * See the {@see 'personal_options'} action. |
|---|
| 509 | * |
|---|
| 510 | * @since 2.7.0 |
|---|
| 511 | * |
|---|
| 512 | * @param object $user User data object |
|---|
| 513 | */ |
|---|
| 514 | function use_ssl_preference($user) { |
|---|
| 515 | ?> |
|---|
| 516 | <tr class="user-use-ssl-wrap"> |
|---|
| 517 | <th scope="row"><?php _e('Use https')?></th> |
|---|
| 518 | <td><label for="use_ssl"><input name="use_ssl" type="checkbox" id="use_ssl" value="1" <?php checked('1', $user->use_ssl); ?> /> <?php _e('Always use https when visiting the admin'); ?></label></td> |
|---|
| 519 | </tr> |
|---|
| 520 | <?php |
|---|
| 521 | } |
|---|
| 522 | |
|---|
| 523 | /** |
|---|
| 524 | * |
|---|
| 525 | * @param string $text |
|---|
| 526 | * @return string |
|---|
| 527 | */ |
|---|
| 528 | function admin_created_user_email( $text ) { |
|---|
| 529 | $roles = get_editable_roles(); |
|---|
| 530 | $role = $roles[ $_REQUEST['role'] ]; |
|---|
| 531 | /* translators: 1: Site name, 2: site URL, 3: role */ |
|---|
| 532 | return sprintf( __( 'Hi, |
|---|
| 533 | You\'ve been invited to join \'%1$s\' at |
|---|
| 534 | %2$s with the role of %3$s. |
|---|
| 535 | If you do not want to join this site please ignore |
|---|
| 536 | this email. This invitation will expire in a few days. |
|---|
| 537 | |
|---|
| 538 | Please click the following link to activate your user account: |
|---|
| 539 | %%s' ), wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ), home_url(), wp_specialchars_decode( translate_user_role( $role['name'] ) ) ); |
|---|
| 540 | } |
|---|
| 541 | |
|---|
| 542 | /** |
|---|
| 543 | * Resend an existing request and return the result. |
|---|
| 544 | * |
|---|
| 545 | * @since 4.9.6 |
|---|
| 546 | * @access private |
|---|
| 547 | * |
|---|
| 548 | * @param int $request_id Request ID. |
|---|
| 549 | * @return bool|WP_Error Returns true/false based on the success of sending the email, or a WP_Error object. |
|---|
| 550 | */ |
|---|
| 551 | function _wp_privacy_resend_request( $request_id ) { |
|---|
| 552 | $request_id = absint( $request_id ); |
|---|
| 553 | $request = get_post( $request_id ); |
|---|
| 554 | |
|---|
| 555 | if ( ! $request || 'user_request' !== $request->post_type ) { |
|---|
| 556 | return new WP_Error( 'privacy_request_error', __( 'Invalid request.' ) ); |
|---|
| 557 | } |
|---|
| 558 | |
|---|
| 559 | $result = wp_send_user_request( $request_id ); |
|---|
| 560 | |
|---|
| 561 | if ( is_wp_error( $result ) ) { |
|---|
| 562 | return $result; |
|---|
| 563 | } elseif ( ! $result ) { |
|---|
| 564 | return new WP_Error( 'privacy_request_error', __( 'Unable to initiate confirmation request.' ) ); |
|---|
| 565 | } |
|---|
| 566 | |
|---|
| 567 | return true; |
|---|
| 568 | } |
|---|
| 569 | |
|---|
| 570 | /** |
|---|
| 571 | * Marks a request as completed by the admin and logs the current timestamp. |
|---|
| 572 | * |
|---|
| 573 | * @since 4.9.6 |
|---|
| 574 | * @access private |
|---|
| 575 | * |
|---|
| 576 | * @param int $request_id Request ID. |
|---|
| 577 | * @return int|WP_Error $request Request ID on success or WP_Error. |
|---|
| 578 | */ |
|---|
| 579 | function _wp_privacy_completed_request( $request_id ) { |
|---|
| 580 | $request_id = absint( $request_id ); |
|---|
| 581 | $request_data = wp_get_user_request_data( $request_id ); |
|---|
| 582 | |
|---|
| 583 | if ( ! $request_data ) { |
|---|
| 584 | return new WP_Error( 'privacy_request_error', __( 'Invalid request.' ) ); |
|---|
| 585 | } |
|---|
| 586 | |
|---|
| 587 | update_post_meta( $request_id, '_wp_user_request_completed_timestamp', time() ); |
|---|
| 588 | |
|---|
| 589 | $request = wp_update_post( array( |
|---|
| 590 | 'ID' => $request_id, |
|---|
| 591 | 'post_status' => 'request-completed', |
|---|
| 592 | ) ); |
|---|
| 593 | |
|---|
| 594 | return $request; |
|---|
| 595 | } |
|---|
| 596 | |
|---|
| 597 | /** |
|---|
| 598 | * Handle list table actions. |
|---|
| 599 | * |
|---|
| 600 | * @since 4.9.6 |
|---|
| 601 | * @access private |
|---|
| 602 | */ |
|---|
| 603 | function _wp_personal_data_handle_actions() { |
|---|
| 604 | if ( isset( $_POST['privacy_action_email_retry'] ) ) { |
|---|
| 605 | check_admin_referer( 'bulk-privacy_requests' ); |
|---|
| 606 | |
|---|
| 607 | $request_id = absint( current( array_keys( (array) wp_unslash( $_POST['privacy_action_email_retry'] ) ) ) ); |
|---|
| 608 | $result = _wp_privacy_resend_request( $request_id ); |
|---|
| 609 | |
|---|
| 610 | if ( is_wp_error( $result ) ) { |
|---|
| 611 | add_settings_error( |
|---|
| 612 | 'privacy_action_email_retry', |
|---|
| 613 | 'privacy_action_email_retry', |
|---|
| 614 | $result->get_error_message(), |
|---|
| 615 | 'error' |
|---|
| 616 | ); |
|---|
| 617 | } else { |
|---|
| 618 | add_settings_error( |
|---|
| 619 | 'privacy_action_email_retry', |
|---|
| 620 | 'privacy_action_email_retry', |
|---|
| 621 | __( 'Confirmation request sent again successfully.' ), |
|---|
| 622 | 'updated' |
|---|
| 623 | ); |
|---|
| 624 | } |
|---|
| 625 | } elseif ( isset( $_POST['action'] ) ) { |
|---|
| 626 | $action = isset( $_POST['action'] ) ? sanitize_key( wp_unslash( $_POST['action'] ) ) : ''; |
|---|
| 627 | |
|---|
| 628 | switch ( $action ) { |
|---|
| 629 | case 'add_export_personal_data_request': |
|---|
| 630 | case 'add_remove_personal_data_request': |
|---|
| 631 | check_admin_referer( 'personal-data-request' ); |
|---|
| 632 | |
|---|
| 633 | if ( ! isset( $_POST['type_of_action'], $_POST['username_or_email_for_privacy_request'] ) ) { |
|---|
| 634 | add_settings_error( |
|---|
| 635 | 'action_type', |
|---|
| 636 | 'action_type', |
|---|
| 637 | __( 'Invalid action.' ), |
|---|
| 638 | 'error' |
|---|
| 639 | ); |
|---|
| 640 | } |
|---|
| 641 | $action_type = sanitize_text_field( wp_unslash( $_POST['type_of_action'] ) ); |
|---|
| 642 | $username_or_email_address = sanitize_text_field( wp_unslash( $_POST['username_or_email_for_privacy_request'] ) ); |
|---|
| 643 | $email_address = ''; |
|---|
| 644 | |
|---|
| 645 | if ( ! in_array( $action_type, _wp_privacy_action_request_types(), true ) ) { |
|---|
| 646 | add_settings_error( |
|---|
| 647 | 'action_type', |
|---|
| 648 | 'action_type', |
|---|
| 649 | __( 'Invalid action.' ), |
|---|
| 650 | 'error' |
|---|
| 651 | ); |
|---|
| 652 | } |
|---|
| 653 | |
|---|
| 654 | if ( ! is_email( $username_or_email_address ) ) { |
|---|
| 655 | $user = get_user_by( 'login', $username_or_email_address ); |
|---|
| 656 | if ( ! $user instanceof WP_User ) { |
|---|
| 657 | add_settings_error( |
|---|
| 658 | 'username_or_email_for_privacy_request', |
|---|
| 659 | 'username_or_email_for_privacy_request', |
|---|
| 660 | __( 'Unable to add this request. A valid email address or username must be supplied.' ), |
|---|
| 661 | 'error' |
|---|
| 662 | ); |
|---|
| 663 | } else { |
|---|
| 664 | $email_address = $user->user_email; |
|---|
| 665 | } |
|---|
| 666 | } else { |
|---|
| 667 | $email_address = $username_or_email_address; |
|---|
| 668 | } |
|---|
| 669 | |
|---|
| 670 | if ( empty( $email_address ) ) { |
|---|
| 671 | break; |
|---|
| 672 | } |
|---|
| 673 | |
|---|
| 674 | $request_id = wp_create_user_request( $email_address, $action_type ); |
|---|
| 675 | |
|---|
| 676 | if ( is_wp_error( $request_id ) ) { |
|---|
| 677 | add_settings_error( |
|---|
| 678 | 'username_or_email_for_privacy_request', |
|---|
| 679 | 'username_or_email_for_privacy_request', |
|---|
| 680 | $request_id->get_error_message(), |
|---|
| 681 | 'error' |
|---|
| 682 | ); |
|---|
| 683 | break; |
|---|
| 684 | } elseif ( ! $request_id ) { |
|---|
| 685 | add_settings_error( |
|---|
| 686 | 'username_or_email_for_privacy_request', |
|---|
| 687 | 'username_or_email_for_privacy_request', |
|---|
| 688 | __( 'Unable to initiate confirmation request.' ), |
|---|
| 689 | 'error' |
|---|
| 690 | ); |
|---|
| 691 | break; |
|---|
| 692 | } |
|---|
| 693 | |
|---|
| 694 | wp_send_user_request( $request_id ); |
|---|
| 695 | |
|---|
| 696 | add_settings_error( |
|---|
| 697 | 'username_or_email_for_privacy_request', |
|---|
| 698 | 'username_or_email_for_privacy_request', |
|---|
| 699 | __( 'Confirmation request initiated successfully.' ), |
|---|
| 700 | 'updated' |
|---|
| 701 | ); |
|---|
| 702 | break; |
|---|
| 703 | } |
|---|
| 704 | } |
|---|
| 705 | } |
|---|
| 706 | |
|---|
| 707 | /** |
|---|
| 708 | * Cleans up failed and expired requests before displaying the list table. |
|---|
| 709 | * |
|---|
| 710 | * @since 4.9.6 |
|---|
| 711 | * @access private |
|---|
| 712 | */ |
|---|
| 713 | function _wp_personal_data_cleanup_requests() { |
|---|
| 714 | /** This filter is documented in wp-includes/user.php */ |
|---|
| 715 | $expires = (int) apply_filters( 'user_request_key_expiration', DAY_IN_SECONDS ); |
|---|
| 716 | |
|---|
| 717 | $requests_query = new WP_Query( array( |
|---|
| 718 | 'post_type' => 'user_request', |
|---|
| 719 | 'posts_per_page' => -1, |
|---|
| 720 | 'post_status' => 'request-pending', |
|---|
| 721 | 'fields' => 'ids', |
|---|
| 722 | 'date_query' => array( |
|---|
| 723 | array( |
|---|
| 724 | 'column' => 'post_modified_gmt', |
|---|
| 725 | 'before' => $expires . ' seconds ago', |
|---|
| 726 | ), |
|---|
| 727 | ), |
|---|
| 728 | ) ); |
|---|
| 729 | |
|---|
| 730 | $request_ids = $requests_query->posts; |
|---|
| 731 | |
|---|
| 732 | foreach ( $request_ids as $request_id ) { |
|---|
| 733 | wp_update_post( array( |
|---|
| 734 | 'ID' => $request_id, |
|---|
| 735 | 'post_status' => 'request-failed', |
|---|
| 736 | 'post_password' => '', |
|---|
| 737 | ) ); |
|---|
| 738 | } |
|---|
| 739 | } |
|---|
| 740 | |
|---|
| 741 | /** |
|---|
| 742 | * Personal data export. |
|---|
| 743 | * |
|---|
| 744 | * @since 4.9.6 |
|---|
| 745 | * @access private |
|---|
| 746 | */ |
|---|
| 747 | function _wp_personal_data_export_page() { |
|---|
| 748 | if ( ! current_user_can( 'export_others_personal_data' ) ) { |
|---|
| 749 | wp_die( __( 'Sorry, you are not allowed to export personal data on this site.' ) ); |
|---|
| 750 | } |
|---|
| 751 | |
|---|
| 752 | _wp_personal_data_handle_actions(); |
|---|
| 753 | _wp_personal_data_cleanup_requests(); |
|---|
| 754 | |
|---|
| 755 | // "Borrow" xfn.js for now so we don't have to create new files. |
|---|
| 756 | wp_enqueue_script( 'xfn' ); |
|---|
| 757 | |
|---|
| 758 | $requests_table = new WP_Privacy_Data_Export_Requests_Table( array( |
|---|
| 759 | 'plural' => 'privacy_requests', |
|---|
| 760 | 'singular' => 'privacy_request', |
|---|
| 761 | ) ); |
|---|
| 762 | $requests_table->process_bulk_action(); |
|---|
| 763 | $requests_table->prepare_items(); |
|---|
| 764 | ?> |
|---|
| 765 | <div class="wrap nosubsub"> |
|---|
| 766 | <h1><?php esc_html_e( 'Export Personal Data' ); ?></h1> |
|---|
| 767 | <hr class="wp-header-end" /> |
|---|
| 768 | |
|---|
| 769 | <?php settings_errors(); ?> |
|---|
| 770 | |
|---|
| 771 | <form method="post" class="wp-privacy-request-form"> |
|---|
| 772 | <h2><?php esc_html_e( 'Add Data Export Request' ); ?></h2> |
|---|
| 773 | <p><?php esc_html_e( 'An email will be sent to the user at this email address asking them to verify the request.' ); ?></p> |
|---|
| 774 | |
|---|
| 775 | <div class="wp-privacy-request-form-field"> |
|---|
| 776 | <label for="username_or_email_for_privacy_request"><?php esc_html_e( 'Username or email address' ); ?></label> |
|---|
| 777 | <input type="text" required class="regular-text" id="username_or_email_for_privacy_request" name="username_or_email_for_privacy_request" /> |
|---|
| 778 | <?php submit_button( __( 'Send Request' ), 'secondary', 'submit', false ); ?> |
|---|
| 779 | </div> |
|---|
| 780 | <?php wp_nonce_field( 'personal-data-request' ); ?> |
|---|
| 781 | <input type="hidden" name="action" value="add_export_personal_data_request" /> |
|---|
| 782 | <input type="hidden" name="type_of_action" value="export_personal_data" /> |
|---|
| 783 | </form> |
|---|
| 784 | <hr /> |
|---|
| 785 | |
|---|
| 786 | <?php $requests_table->views(); ?> |
|---|
| 787 | |
|---|
| 788 | <form class="search-form wp-clearfix"> |
|---|
| 789 | <?php $requests_table->search_box( __( 'Search Requests' ), 'requests' ); ?> |
|---|
| 790 | <input type="hidden" name="page" value="export_personal_data" /> |
|---|
| 791 | <input type="hidden" name="filter-status" value="<?php echo isset( $_REQUEST['filter-status'] ) ? esc_attr( sanitize_text_field( $_REQUEST['filter-status'] ) ) : ''; ?>" /> |
|---|
| 792 | <input type="hidden" name="orderby" value="<?php echo isset( $_REQUEST['orderby'] ) ? esc_attr( sanitize_text_field( $_REQUEST['orderby'] ) ) : ''; ?>" /> |
|---|
| 793 | <input type="hidden" name="order" value="<?php echo isset( $_REQUEST['order'] ) ? esc_attr( sanitize_text_field( $_REQUEST['order'] ) ) : ''; ?>" /> |
|---|
| 794 | </form> |
|---|
| 795 | |
|---|
| 796 | <form method="post"> |
|---|
| 797 | <?php |
|---|
| 798 | $requests_table->display(); |
|---|
| 799 | $requests_table->embed_scripts(); |
|---|
| 800 | ?> |
|---|
| 801 | </form> |
|---|
| 802 | </div> |
|---|
| 803 | <?php |
|---|
| 804 | } |
|---|
| 805 | |
|---|
| 806 | /** |
|---|
| 807 | * Personal data anonymization. |
|---|
| 808 | * |
|---|
| 809 | * @since 4.9.6 |
|---|
| 810 | * @access private |
|---|
| 811 | */ |
|---|
| 812 | function _wp_personal_data_removal_page() { |
|---|
| 813 | /* |
|---|
| 814 | * Require both caps in order to make it explicitly clear that delegating |
|---|
| 815 | * erasure from network admins to single-site admins will give them the |
|---|
| 816 | * ability to affect global users, rather than being limited to the site |
|---|
| 817 | * that they administer. |
|---|
| 818 | */ |
|---|
| 819 | if ( ! current_user_can( 'erase_others_personal_data' ) || ! current_user_can( 'delete_users' ) ) { |
|---|
| 820 | wp_die( __( 'Sorry, you are not allowed to erase data on this site.' ) ); |
|---|
| 821 | } |
|---|
| 822 | |
|---|
| 823 | _wp_personal_data_handle_actions(); |
|---|
| 824 | _wp_personal_data_cleanup_requests(); |
|---|
| 825 | |
|---|
| 826 | // "Borrow" xfn.js for now so we don't have to create new files. |
|---|
| 827 | wp_enqueue_script( 'xfn' ); |
|---|
| 828 | |
|---|
| 829 | $requests_table = new WP_Privacy_Data_Removal_Requests_Table( array( |
|---|
| 830 | 'plural' => 'privacy_requests', |
|---|
| 831 | 'singular' => 'privacy_request', |
|---|
| 832 | ) ); |
|---|
| 833 | |
|---|
| 834 | $requests_table->process_bulk_action(); |
|---|
| 835 | $requests_table->prepare_items(); |
|---|
| 836 | |
|---|
| 837 | ?> |
|---|
| 838 | <div class="wrap nosubsub"> |
|---|
| 839 | <h1><?php esc_html_e( 'Erase Personal Data' ); ?></h1> |
|---|
| 840 | <hr class="wp-header-end" /> |
|---|
| 841 | |
|---|
| 842 | <?php settings_errors(); ?> |
|---|
| 843 | |
|---|
| 844 | <form method="post" class="wp-privacy-request-form"> |
|---|
| 845 | <h2><?php esc_html_e( 'Add Data Erasure Request' ); ?></h2> |
|---|
| 846 | <p><?php esc_html_e( 'An email will be sent to the user at this email address asking them to verify the request.' ); ?></p> |
|---|
| 847 | |
|---|
| 848 | <div class="wp-privacy-request-form-field"> |
|---|
| 849 | <label for="username_or_email_for_privacy_request"><?php esc_html_e( 'Username or email address' ); ?></label> |
|---|
| 850 | <input type="text" required class="regular-text" id="username_or_email_for_privacy_request" name="username_or_email_for_privacy_request" /> |
|---|
| 851 | <?php submit_button( __( 'Send Request' ), 'secondary', 'submit', false ); ?> |
|---|
| 852 | </div> |
|---|
| 853 | <?php wp_nonce_field( 'personal-data-request' ); ?> |
|---|
| 854 | <input type="hidden" name="action" value="add_remove_personal_data_request" /> |
|---|
| 855 | <input type="hidden" name="type_of_action" value="remove_personal_data" /> |
|---|
| 856 | </form> |
|---|
| 857 | <hr /> |
|---|
| 858 | |
|---|
| 859 | <?php $requests_table->views(); ?> |
|---|
| 860 | |
|---|
| 861 | <form class="search-form wp-clearfix"> |
|---|
| 862 | <?php $requests_table->search_box( __( 'Search Requests' ), 'requests' ); ?> |
|---|
| 863 | <input type="hidden" name="page" value="remove_personal_data" /> |
|---|
| 864 | <input type="hidden" name="filter-status" value="<?php echo isset( $_REQUEST['filter-status'] ) ? esc_attr( sanitize_text_field( $_REQUEST['filter-status'] ) ) : ''; ?>" /> |
|---|
| 865 | <input type="hidden" name="orderby" value="<?php echo isset( $_REQUEST['orderby'] ) ? esc_attr( sanitize_text_field( $_REQUEST['orderby'] ) ) : ''; ?>" /> |
|---|
| 866 | <input type="hidden" name="order" value="<?php echo isset( $_REQUEST['order'] ) ? esc_attr( sanitize_text_field( $_REQUEST['order'] ) ) : ''; ?>" /> |
|---|
| 867 | </form> |
|---|
| 868 | |
|---|
| 869 | <form method="post"> |
|---|
| 870 | <?php |
|---|
| 871 | $requests_table->display(); |
|---|
| 872 | $requests_table->embed_scripts(); |
|---|
| 873 | ?> |
|---|
| 874 | </form> |
|---|
| 875 | </div> |
|---|
| 876 | <?php |
|---|
| 877 | } |
|---|
| 878 | |
|---|
| 879 | /** |
|---|
| 880 | * Mark erasure requests as completed after processing is finished. |
|---|
| 881 | * |
|---|
| 882 | * This intercepts the Ajax responses to personal data eraser page requests, and |
|---|
| 883 | * monitors the status of a request. Once all of the processing has finished, the |
|---|
| 884 | * request is marked as completed. |
|---|
| 885 | * |
|---|
| 886 | * @since 4.9.6 |
|---|
| 887 | * |
|---|
| 888 | * @see wp_privacy_personal_data_erasure_page |
|---|
| 889 | * |
|---|
| 890 | * @param array $response The response from the personal data eraser for |
|---|
| 891 | * the given page. |
|---|
| 892 | * @param int $eraser_index The index of the personal data eraser. Begins |
|---|
| 893 | * at 1. |
|---|
| 894 | * @param string $email_address The email address of the user whose personal |
|---|
| 895 | * data this is. |
|---|
| 896 | * @param int $page The page of personal data for this eraser. |
|---|
| 897 | * Begins at 1. |
|---|
| 898 | * @param int $request_id The request ID for this personal data erasure. |
|---|
| 899 | * @return array The filtered response. |
|---|
| 900 | */ |
|---|
| 901 | function wp_privacy_process_personal_data_erasure_page( $response, $eraser_index, $email_address, $page, $request_id ) { |
|---|
| 902 | /* |
|---|
| 903 | * If the eraser response is malformed, don't attempt to consume it; let it |
|---|
| 904 | * pass through, so that the default Ajax processing will generate a warning |
|---|
| 905 | * to the user. |
|---|
| 906 | */ |
|---|
| 907 | if ( ! is_array( $response ) ) { |
|---|
| 908 | return $response; |
|---|
| 909 | } |
|---|
| 910 | |
|---|
| 911 | if ( ! array_key_exists( 'done', $response ) ) { |
|---|
| 912 | return $response; |
|---|
| 913 | } |
|---|
| 914 | |
|---|
| 915 | if ( ! array_key_exists( 'items_removed', $response ) ) { |
|---|
| 916 | return $response; |
|---|
| 917 | } |
|---|
| 918 | |
|---|
| 919 | if ( ! array_key_exists( 'items_retained', $response ) ) { |
|---|
| 920 | return $response; |
|---|
| 921 | } |
|---|
| 922 | |
|---|
| 923 | if ( ! array_key_exists( 'messages', $response ) ) { |
|---|
| 924 | return $response; |
|---|
| 925 | } |
|---|
| 926 | |
|---|
| 927 | $request = wp_get_user_request_data( $request_id ); |
|---|
| 928 | |
|---|
| 929 | if ( ! $request || 'remove_personal_data' !== $request->action_name ) { |
|---|
| 930 | wp_send_json_error( __( 'Invalid request ID when processing eraser data.' ) ); |
|---|
| 931 | } |
|---|
| 932 | |
|---|
| 933 | /** This filter is documented in wp-admin/includes/ajax-actions.php */ |
|---|
| 934 | $erasers = apply_filters( 'wp_privacy_personal_data_erasers', array() ); |
|---|
| 935 | $is_last_eraser = count( $erasers ) === $eraser_index; |
|---|
| 936 | $eraser_done = $response['done']; |
|---|
| 937 | |
|---|
| 938 | if ( ! $is_last_eraser || ! $eraser_done ) { |
|---|
| 939 | return $response; |
|---|
| 940 | } |
|---|
| 941 | |
|---|
| 942 | _wp_privacy_completed_request( $request_id ); |
|---|
| 943 | |
|---|
| 944 | /** |
|---|
| 945 | * Fires immediately after a personal data erasure request has been marked completed. |
|---|
| 946 | * |
|---|
| 947 | * @since 4.9.6 |
|---|
| 948 | * |
|---|
| 949 | * @param int $request_id The privacy request post ID associated with this request. |
|---|
| 950 | */ |
|---|
| 951 | do_action( 'wp_privacy_personal_data_erased', $request_id ); |
|---|
| 952 | |
|---|
| 953 | return $response; |
|---|
| 954 | } |
|---|
| 955 | |
|---|
| 956 | /** |
|---|
| 957 | * Add requests pages. |
|---|
| 958 | * |
|---|
| 959 | * @since 4.9.6 |
|---|
| 960 | * @access private |
|---|
| 961 | */ |
|---|
| 962 | function _wp_privacy_hook_requests_page() { |
|---|
| 963 | add_submenu_page( 'tools.php', __( 'Export Personal Data' ), __( 'Export Personal Data' ), 'export_others_personal_data', 'export_personal_data', '_wp_personal_data_export_page' ); |
|---|
| 964 | add_submenu_page( 'tools.php', __( 'Erase Personal Data' ), __( 'Erase Personal Data' ), 'erase_others_personal_data', 'remove_personal_data', '_wp_personal_data_removal_page' ); |
|---|
| 965 | } |
|---|
| 966 | |
|---|
| 967 | /** |
|---|
| 968 | * Add options for the privacy requests screens. |
|---|
| 969 | * |
|---|
| 970 | * @since 4.9.8 |
|---|
| 971 | * @access private |
|---|
| 972 | */ |
|---|
| 973 | function _wp_privacy_requests_screen_options() { |
|---|
| 974 | $args = array( |
|---|
| 975 | 'option' => str_replace( 'tools_page_', '', get_current_screen()->id ) . '_requests_per_page', |
|---|
| 976 | ); |
|---|
| 977 | add_screen_option( 'per_page', $args ); |
|---|
| 978 | } |
|---|
| 979 | |
|---|
| 980 | // TODO: move the following classes in new files. |
|---|
| 981 | if ( ! class_exists( 'WP_List_Table' ) ) { |
|---|
| 982 | require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' ); |
|---|
| 983 | } |
|---|
| 984 | |
|---|
| 985 | /** |
|---|
| 986 | * WP_Privacy_Requests_Table class. |
|---|
| 987 | * |
|---|
| 988 | * @since 4.9.6 |
|---|
| 989 | */ |
|---|
| 990 | abstract class WP_Privacy_Requests_Table extends WP_List_Table { |
|---|
| 991 | |
|---|
| 992 | /** |
|---|
| 993 | * Action name for the requests this table will work with. Classes |
|---|
| 994 | * which inherit from WP_Privacy_Requests_Table should define this. |
|---|
| 995 | * |
|---|
| 996 | * Example: 'export_personal_data'. |
|---|
| 997 | * |
|---|
| 998 | * @since 4.9.6 |
|---|
| 999 | * |
|---|
| 1000 | * @var string $request_type Name of action. |
|---|
| 1001 | */ |
|---|
| 1002 | protected $request_type = 'INVALID'; |
|---|
| 1003 | |
|---|
| 1004 | /** |
|---|
| 1005 | * Post type to be used. |
|---|
| 1006 | * |
|---|
| 1007 | * @since 4.9.6 |
|---|
| 1008 | * |
|---|
| 1009 | * @var string $post_type The post type. |
|---|
| 1010 | */ |
|---|
| 1011 | protected $post_type = 'INVALID'; |
|---|
| 1012 | |
|---|
| 1013 | /** |
|---|
| 1014 | * Get columns to show in the list table. |
|---|
| 1015 | * |
|---|
| 1016 | * @since 4.9.6 |
|---|
| 1017 | * |
|---|
| 1018 | * @return array Array of columns. |
|---|
| 1019 | */ |
|---|
| 1020 | public function get_columns() { |
|---|
| 1021 | $columns = array( |
|---|
| 1022 | 'cb' => '<input type="checkbox" />', |
|---|
| 1023 | 'email' => __( 'Requester' ), |
|---|
| 1024 | 'status' => __( 'Status' ), |
|---|
| 1025 | 'created_timestamp' => __( 'Requested' ), |
|---|
| 1026 | 'next_steps' => __( 'Next Steps' ), |
|---|
| 1027 | ); |
|---|
| 1028 | return $columns; |
|---|
| 1029 | } |
|---|
| 1030 | |
|---|
| 1031 | /** |
|---|
| 1032 | * Get a list of sortable columns. |
|---|
| 1033 | * |
|---|
| 1034 | * @since 4.9.6 |
|---|
| 1035 | * |
|---|
| 1036 | * @return array Default sortable columns. |
|---|
| 1037 | */ |
|---|
| 1038 | protected function get_sortable_columns() { |
|---|
| 1039 | return array(); |
|---|
| 1040 | } |
|---|
| 1041 | |
|---|
| 1042 | /** |
|---|
| 1043 | * Default primary column. |
|---|
| 1044 | * |
|---|
| 1045 | * @since 4.9.6 |
|---|
| 1046 | * |
|---|
| 1047 | * @return string Default primary column name. |
|---|
| 1048 | */ |
|---|
| 1049 | protected function get_default_primary_column_name() { |
|---|
| 1050 | return 'email'; |
|---|
| 1051 | } |
|---|
| 1052 | |
|---|
| 1053 | /** |
|---|
| 1054 | * Count number of requests for each status. |
|---|
| 1055 | * |
|---|
| 1056 | * @since 4.9.6 |
|---|
| 1057 | * |
|---|
| 1058 | * @return object Number of posts for each status. |
|---|
| 1059 | */ |
|---|
| 1060 | protected function get_request_counts() { |
|---|
| 1061 | global $wpdb; |
|---|
| 1062 | |
|---|
| 1063 | $cache_key = $this->post_type . '-' . $this->request_type; |
|---|
| 1064 | $counts = wp_cache_get( $cache_key, 'counts' ); |
|---|
| 1065 | |
|---|
| 1066 | if ( false !== $counts ) { |
|---|
| 1067 | return $counts; |
|---|
| 1068 | } |
|---|
| 1069 | |
|---|
| 1070 | $query = " |
|---|
| 1071 | SELECT post_status, COUNT( * ) AS num_posts |
|---|
| 1072 | FROM {$wpdb->posts} |
|---|
| 1073 | WHERE post_type = %s |
|---|
| 1074 | AND post_name = %s |
|---|
| 1075 | GROUP BY post_status"; |
|---|
| 1076 | |
|---|
| 1077 | $results = (array) $wpdb->get_results( $wpdb->prepare( $query, $this->post_type, $this->request_type ), ARRAY_A ); |
|---|
| 1078 | $counts = array_fill_keys( get_post_stati(), 0 ); |
|---|
| 1079 | |
|---|
| 1080 | foreach ( $results as $row ) { |
|---|
| 1081 | $counts[ $row['post_status'] ] = $row['num_posts']; |
|---|
| 1082 | } |
|---|
| 1083 | |
|---|
| 1084 | $counts = (object) $counts; |
|---|
| 1085 | wp_cache_set( $cache_key, $counts, 'counts' ); |
|---|
| 1086 | |
|---|
| 1087 | return $counts; |
|---|
| 1088 | } |
|---|
| 1089 | |
|---|
| 1090 | /** |
|---|
| 1091 | * Get an associative array ( id => link ) with the list of views available on this table. |
|---|
| 1092 | * |
|---|
| 1093 | * @since 4.9.6 |
|---|
| 1094 | * |
|---|
| 1095 | * @return array Associative array of views in the format of $view_name => $view_markup. |
|---|
| 1096 | */ |
|---|
| 1097 | protected function get_views() { |
|---|
| 1098 | $current_status = isset( $_REQUEST['filter-status'] ) ? sanitize_text_field( $_REQUEST['filter-status'] ) : ''; |
|---|
| 1099 | $statuses = _wp_privacy_statuses(); |
|---|
| 1100 | $views = array(); |
|---|
| 1101 | $admin_url = admin_url( 'tools.php?page=' . $this->request_type ); |
|---|
| 1102 | $counts = $this->get_request_counts(); |
|---|
| 1103 | $total_requests = absint( array_sum( (array) $counts ) ); |
|---|
| 1104 | |
|---|
| 1105 | $current_link_attributes = empty( $current_status ) ? ' class="current" aria-current="page"' : ''; |
|---|
| 1106 | $views['all'] = '<a href="' . esc_url( $admin_url ) . "\" $current_link_attributes>" . sprintf( _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%d)</span>', $total_requests, 'requests' ), number_format_i18n( $total_requests ) ) . '</a>'; |
|---|
| 1107 | |
|---|
| 1108 | foreach ( $statuses as $status => $label ) { |
|---|
| 1109 | $current_link_attributes = $status === $current_status ? ' class="current" aria-current="page"' : ''; |
|---|
| 1110 | $total_status_requests = absint( $counts->$status ); |
|---|
| 1111 | $views[ $status ] = '<a href="' . esc_url( add_query_arg( 'filter-status', $status, $admin_url ) ) . "\" $current_link_attributes>" . sprintf( _nx( '%s <span class="count">(%d)</span>', '%s <span class="count">(%d)</span>', $total_status_requests, 'requests' ), esc_html( $label ), number_format_i18n( $total_status_requests ) ) . '</a>'; |
|---|
| 1112 | } |
|---|
| 1113 | |
|---|
| 1114 | return $views; |
|---|
| 1115 | } |
|---|
| 1116 | |
|---|
| 1117 | /** |
|---|
| 1118 | * Get bulk actions. |
|---|
| 1119 | * |
|---|
| 1120 | * @since 4.9.6 |
|---|
| 1121 | * |
|---|
| 1122 | * @return array List of bulk actions. |
|---|
| 1123 | */ |
|---|
| 1124 | protected function get_bulk_actions() { |
|---|
| 1125 | return array( |
|---|
| 1126 | 'delete' => __( 'Remove' ), |
|---|
| 1127 | 'resend' => __( 'Resend email' ), |
|---|
| 1128 | ); |
|---|
| 1129 | } |
|---|
| 1130 | |
|---|
| 1131 | /** |
|---|
| 1132 | * Process bulk actions. |
|---|
| 1133 | * |
|---|
| 1134 | * @since 4.9.6 |
|---|
| 1135 | */ |
|---|
| 1136 | public function process_bulk_action() { |
|---|
| 1137 | $action = $this->current_action(); |
|---|
| 1138 | $request_ids = isset( $_REQUEST['request_id'] ) ? wp_parse_id_list( wp_unslash( $_REQUEST['request_id'] ) ) : array(); |
|---|
| 1139 | |
|---|
| 1140 | $count = 0; |
|---|
| 1141 | |
|---|
| 1142 | if ( $request_ids ) { |
|---|
| 1143 | check_admin_referer( 'bulk-privacy_requests' ); |
|---|
| 1144 | } |
|---|
| 1145 | |
|---|
| 1146 | switch ( $action ) { |
|---|
| 1147 | case 'delete': |
|---|
| 1148 | foreach ( $request_ids as $request_id ) { |
|---|
| 1149 | if ( wp_delete_post( $request_id, true ) ) { |
|---|
| 1150 | $count ++; |
|---|
| 1151 | } |
|---|
| 1152 | } |
|---|
| 1153 | |
|---|
| 1154 | add_settings_error( |
|---|
| 1155 | 'bulk_action', |
|---|
| 1156 | 'bulk_action', |
|---|
| 1157 | /* translators: %d: number of requests */ |
|---|
| 1158 | sprintf( _n( 'Deleted %d request', 'Deleted %d requests', $count ), $count ), |
|---|
| 1159 | 'updated' |
|---|
| 1160 | ); |
|---|
| 1161 | break; |
|---|
| 1162 | case 'resend': |
|---|
| 1163 | foreach ( $request_ids as $request_id ) { |
|---|
| 1164 | $resend = _wp_privacy_resend_request( $request_id ); |
|---|
| 1165 | |
|---|
| 1166 | if ( $resend && ! is_wp_error( $resend ) ) { |
|---|
| 1167 | $count++; |
|---|
| 1168 | } |
|---|
| 1169 | } |
|---|
| 1170 | |
|---|
| 1171 | add_settings_error( |
|---|
| 1172 | 'bulk_action', |
|---|
| 1173 | 'bulk_action', |
|---|
| 1174 | /* translators: %d: number of requests */ |
|---|
| 1175 | sprintf( _n( 'Re-sent %d request', 'Re-sent %d requests', $count ), $count ), |
|---|
| 1176 | 'updated' |
|---|
| 1177 | ); |
|---|
| 1178 | break; |
|---|
| 1179 | } |
|---|
| 1180 | } |
|---|
| 1181 | |
|---|
| 1182 | /** |
|---|
| 1183 | * Prepare items to output. |
|---|
| 1184 | * |
|---|
| 1185 | * @since 4.9.6 |
|---|
| 1186 | */ |
|---|
| 1187 | public function prepare_items() { |
|---|
| 1188 | global $wpdb; |
|---|
| 1189 | |
|---|
| 1190 | $primary = $this->get_primary_column_name(); |
|---|
| 1191 | $this->_column_headers = array( |
|---|
| 1192 | $this->get_columns(), |
|---|
| 1193 | array(), |
|---|
| 1194 | $this->get_sortable_columns(), |
|---|
| 1195 | $primary, |
|---|
| 1196 | ); |
|---|
| 1197 | |
|---|
| 1198 | $this->items = array(); |
|---|
| 1199 | $posts_per_page = $this->get_items_per_page( $this->request_type . '_requests_per_page' ); |
|---|
| 1200 | $args = array( |
|---|
| 1201 | 'post_type' => $this->post_type, |
|---|
| 1202 | 'post_name__in' => array( $this->request_type ), |
|---|
| 1203 | 'posts_per_page' => $posts_per_page, |
|---|
| 1204 | 'offset' => isset( $_REQUEST['paged'] ) ? max( 0, absint( $_REQUEST['paged'] ) - 1 ) * $posts_per_page : 0, |
|---|
| 1205 | 'post_status' => 'any', |
|---|
| 1206 | 's' => isset( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : '', |
|---|
| 1207 | ); |
|---|
| 1208 | |
|---|
| 1209 | if ( ! empty( $_REQUEST['filter-status'] ) ) { |
|---|
| 1210 | $filter_status = isset( $_REQUEST['filter-status'] ) ? sanitize_text_field( $_REQUEST['filter-status'] ) : ''; |
|---|
| 1211 | $args['post_status'] = $filter_status; |
|---|
| 1212 | } |
|---|
| 1213 | |
|---|
| 1214 | $requests_query = new WP_Query( $args ); |
|---|
| 1215 | $requests = $requests_query->posts; |
|---|
| 1216 | |
|---|
| 1217 | foreach ( $requests as $request ) { |
|---|
| 1218 | $this->items[] = wp_get_user_request_data( $request->ID ); |
|---|
| 1219 | } |
|---|
| 1220 | |
|---|
| 1221 | $this->items = array_filter( $this->items ); |
|---|
| 1222 | |
|---|
| 1223 | $this->set_pagination_args( |
|---|
| 1224 | array( |
|---|
| 1225 | 'total_items' => $requests_query->found_posts, |
|---|
| 1226 | 'per_page' => $posts_per_page, |
|---|
| 1227 | ) |
|---|
| 1228 | ); |
|---|
| 1229 | } |
|---|
| 1230 | |
|---|
| 1231 | /** |
|---|
| 1232 | * Checkbox column. |
|---|
| 1233 | * |
|---|
| 1234 | * @since 4.9.6 |
|---|
| 1235 | * |
|---|
| 1236 | * @param WP_User_Request $item Item being shown. |
|---|
| 1237 | * @return string Checkbox column markup. |
|---|
| 1238 | */ |
|---|
| 1239 | public function column_cb( $item ) { |
|---|
| 1240 | return sprintf( '<input type="checkbox" name="request_id[]" value="%1$s" /><span class="spinner"></span>', esc_attr( $item->ID ) ); |
|---|
| 1241 | } |
|---|
| 1242 | |
|---|
| 1243 | /** |
|---|
| 1244 | * Status column. |
|---|
| 1245 | * |
|---|
| 1246 | * @since 4.9.6 |
|---|
| 1247 | * |
|---|
| 1248 | * @param WP_User_Request $item Item being shown. |
|---|
| 1249 | * @return string Status column markup. |
|---|
| 1250 | */ |
|---|
| 1251 | public function column_status( $item ) { |
|---|
| 1252 | $status = get_post_status( $item->ID ); |
|---|
| 1253 | $status_object = get_post_status_object( $status ); |
|---|
| 1254 | |
|---|
| 1255 | if ( ! $status_object || empty( $status_object->label ) ) { |
|---|
| 1256 | return '-'; |
|---|
| 1257 | } |
|---|
| 1258 | |
|---|
| 1259 | $timestamp = false; |
|---|
| 1260 | |
|---|
| 1261 | switch ( $status ) { |
|---|
| 1262 | case 'request-confirmed': |
|---|
| 1263 | $timestamp = $item->confirmed_timestamp; |
|---|
| 1264 | break; |
|---|
| 1265 | case 'request-completed': |
|---|
| 1266 | $timestamp = $item->completed_timestamp; |
|---|
| 1267 | break; |
|---|
| 1268 | } |
|---|
| 1269 | |
|---|
| 1270 | echo '<span class="status-label status-' . esc_attr( $status ) . '">'; |
|---|
| 1271 | echo esc_html( $status_object->label ); |
|---|
| 1272 | |
|---|
| 1273 | if ( $timestamp ) { |
|---|
| 1274 | echo ' (' . $this->get_timestamp_as_date( $timestamp ) . ')'; |
|---|
| 1275 | } |
|---|
| 1276 | |
|---|
| 1277 | echo '</span>'; |
|---|
| 1278 | } |
|---|
| 1279 | |
|---|
| 1280 | /** |
|---|
| 1281 | * Convert timestamp for display. |
|---|
| 1282 | * |
|---|
| 1283 | * @since 4.9.6 |
|---|
| 1284 | * |
|---|
| 1285 | * @param int $timestamp Event timestamp. |
|---|
| 1286 | * @return string Human readable date. |
|---|
| 1287 | */ |
|---|
| 1288 | protected function get_timestamp_as_date( $timestamp ) { |
|---|
| 1289 | if ( empty( $timestamp ) ) { |
|---|
| 1290 | return ''; |
|---|
| 1291 | } |
|---|
| 1292 | |
|---|
| 1293 | $time_diff = current_time( 'timestamp', true ) - $timestamp; |
|---|
| 1294 | |
|---|
| 1295 | if ( $time_diff >= 0 && $time_diff < DAY_IN_SECONDS ) { |
|---|
| 1296 | /* translators: human readable timestamp */ |
|---|
| 1297 | return sprintf( __( '%s ago' ), human_time_diff( $timestamp ) ); |
|---|
| 1298 | } |
|---|
| 1299 | |
|---|
| 1300 | return date_i18n( get_option( 'date_format' ), $timestamp ); |
|---|
| 1301 | } |
|---|
| 1302 | |
|---|
| 1303 | /** |
|---|
| 1304 | * Default column handler. |
|---|
| 1305 | * |
|---|
| 1306 | * @since 4.9.6 |
|---|
| 1307 | * |
|---|
| 1308 | * @param WP_User_Request $item Item being shown. |
|---|
| 1309 | * @param string $column_name Name of column being shown. |
|---|
| 1310 | * @return string Default column output. |
|---|
| 1311 | */ |
|---|
| 1312 | public function column_default( $item, $column_name ) { |
|---|
| 1313 | $cell_value = $item->$column_name; |
|---|
| 1314 | |
|---|
| 1315 | if ( in_array( $column_name, array( 'created_timestamp' ), true ) ) { |
|---|
| 1316 | return $this->get_timestamp_as_date( $cell_value ); |
|---|
| 1317 | } |
|---|
| 1318 | |
|---|
| 1319 | return $cell_value; |
|---|
| 1320 | } |
|---|
| 1321 | |
|---|
| 1322 | /** |
|---|
| 1323 | * Actions column. Overridden by children. |
|---|
| 1324 | * |
|---|
| 1325 | * @since 4.9.6 |
|---|
| 1326 | * |
|---|
| 1327 | * @param WP_User_Request $item Item being shown. |
|---|
| 1328 | * @return string Email column markup. |
|---|
| 1329 | */ |
|---|
| 1330 | public function column_email( $item ) { |
|---|
| 1331 | return sprintf( '<a href="%1$s">%2$s</a> %3$s', esc_url( 'mailto:' . $item->email ), $item->email, $this->row_actions( array() ) ); |
|---|
| 1332 | } |
|---|
| 1333 | |
|---|
| 1334 | /** |
|---|
| 1335 | * Next steps column. Overridden by children. |
|---|
| 1336 | * |
|---|
| 1337 | * @since 4.9.6 |
|---|
| 1338 | * |
|---|
| 1339 | * @param WP_User_Request $item Item being shown. |
|---|
| 1340 | */ |
|---|
| 1341 | public function column_next_steps( $item ) {} |
|---|
| 1342 | |
|---|
| 1343 | /** |
|---|
| 1344 | * Generates content for a single row of the table, |
|---|
| 1345 | * |
|---|
| 1346 | * @since 4.9.6 |
|---|
| 1347 | * |
|---|
| 1348 | * @param WP_User_Request $item The current item. |
|---|
| 1349 | */ |
|---|
| 1350 | public function single_row( $item ) { |
|---|
| 1351 | $status = $item->status; |
|---|
| 1352 | |
|---|
| 1353 | echo '<tr id="request-' . esc_attr( $item->ID ) . '" class="status-' . esc_attr( $status ) . '">'; |
|---|
| 1354 | $this->single_row_columns( $item ); |
|---|
| 1355 | echo '</tr>'; |
|---|
| 1356 | } |
|---|
| 1357 | |
|---|
| 1358 | /** |
|---|
| 1359 | * Embed scripts used to perform actions. Overridden by children. |
|---|
| 1360 | * |
|---|
| 1361 | * @since 4.9.6 |
|---|
| 1362 | */ |
|---|
| 1363 | public function embed_scripts() {} |
|---|
| 1364 | } |
|---|
| 1365 | |
|---|
| 1366 | /** |
|---|
| 1367 | * WP_Privacy_Data_Export_Requests_Table class. |
|---|
| 1368 | * |
|---|
| 1369 | * @since 4.9.6 |
|---|
| 1370 | */ |
|---|
| 1371 | class WP_Privacy_Data_Export_Requests_Table extends WP_Privacy_Requests_Table { |
|---|
| 1372 | /** |
|---|
| 1373 | * Action name for the requests this table will work with. |
|---|
| 1374 | * |
|---|
| 1375 | * @since 4.9.6 |
|---|
| 1376 | * |
|---|
| 1377 | * @var string $request_type Name of action. |
|---|
| 1378 | */ |
|---|
| 1379 | protected $request_type = 'export_personal_data'; |
|---|
| 1380 | |
|---|
| 1381 | /** |
|---|
| 1382 | * Post type for the requests. |
|---|
| 1383 | * |
|---|
| 1384 | * @since 4.9.6 |
|---|
| 1385 | * |
|---|
| 1386 | * @var string $post_type The post type. |
|---|
| 1387 | */ |
|---|
| 1388 | protected $post_type = 'user_request'; |
|---|
| 1389 | |
|---|
| 1390 | /** |
|---|
| 1391 | * Actions column. |
|---|
| 1392 | * |
|---|
| 1393 | * @since 4.9.6 |
|---|
| 1394 | * |
|---|
| 1395 | * @param WP_User_Request $item Item being shown. |
|---|
| 1396 | * @return string Email column markup. |
|---|
| 1397 | */ |
|---|
| 1398 | public function column_email( $item ) { |
|---|
| 1399 | /** This filter is documented in wp-admin/includes/ajax-actions.php */ |
|---|
| 1400 | $exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() ); |
|---|
| 1401 | $exporters_count = count( $exporters ); |
|---|
| 1402 | $request_id = $item->ID; |
|---|
| 1403 | $nonce = wp_create_nonce( 'wp-privacy-export-personal-data-' . $request_id ); |
|---|
| 1404 | |
|---|
| 1405 | $download_data_markup = '<div class="export-personal-data" ' . |
|---|
| 1406 | 'data-exporters-count="' . esc_attr( $exporters_count ) . '" ' . |
|---|
| 1407 | 'data-request-id="' . esc_attr( $request_id ) . '" ' . |
|---|
| 1408 | 'data-nonce="' . esc_attr( $nonce ) . |
|---|
| 1409 | '">'; |
|---|
| 1410 | |
|---|
| 1411 | $download_data_markup .= '<span class="export-personal-data-idle"><button type="button" class="button-link export-personal-data-handle">' . __( 'Download Personal Data' ) . '</button></span>' . |
|---|
| 1412 | '<span style="display:none" class="export-personal-data-processing" >' . __( 'Downloading Data...' ) . '</span>' . |
|---|
| 1413 | '<span style="display:none" class="export-personal-data-success"><button type="button" class="button-link export-personal-data-handle">' . __( 'Download Personal Data Again' ) . '</button></span>' . |
|---|
| 1414 | '<span style="display:none" class="export-personal-data-failed">' . __( 'Download has failed.' ) . ' <button type="button" class="button-link">' . __( 'Retry' ) . '</button></span>'; |
|---|
| 1415 | |
|---|
| 1416 | $download_data_markup .= '</div>'; |
|---|
| 1417 | |
|---|
| 1418 | $row_actions = array( |
|---|
| 1419 | 'download-data' => $download_data_markup, |
|---|
| 1420 | ); |
|---|
| 1421 | |
|---|
| 1422 | return sprintf( '<a href="%1$s">%2$s</a> %3$s', esc_url( 'mailto:' . $item->email ), $item->email, $this->row_actions( $row_actions ) ); |
|---|
| 1423 | } |
|---|
| 1424 | |
|---|
| 1425 | /** |
|---|
| 1426 | * Displays the next steps column. |
|---|
| 1427 | * |
|---|
| 1428 | * @since 4.9.6 |
|---|
| 1429 | * |
|---|
| 1430 | * @param WP_User_Request $item Item being shown. |
|---|
| 1431 | */ |
|---|
| 1432 | public function column_next_steps( $item ) { |
|---|
| 1433 | $status = $item->status; |
|---|
| 1434 | |
|---|
| 1435 | switch ( $status ) { |
|---|
| 1436 | case 'request-pending': |
|---|
| 1437 | esc_html_e( 'Waiting for confirmation' ); |
|---|
| 1438 | break; |
|---|
| 1439 | case 'request-confirmed': |
|---|
| 1440 | /** This filter is documented in wp-admin/includes/ajax-actions.php */ |
|---|
| 1441 | $exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() ); |
|---|
| 1442 | $exporters_count = count( $exporters ); |
|---|
| 1443 | $request_id = $item->ID; |
|---|
| 1444 | $nonce = wp_create_nonce( 'wp-privacy-export-personal-data-' . $request_id ); |
|---|
| 1445 | |
|---|
| 1446 | echo '<div class="export-personal-data" ' . |
|---|
| 1447 | 'data-send-as-email="1" ' . |
|---|
| 1448 | 'data-exporters-count="' . esc_attr( $exporters_count ) . '" ' . |
|---|
| 1449 | 'data-request-id="' . esc_attr( $request_id ) . '" ' . |
|---|
| 1450 | 'data-nonce="' . esc_attr( $nonce ) . |
|---|
| 1451 | '">'; |
|---|
| 1452 | |
|---|
| 1453 | ?> |
|---|
| 1454 | <span class="export-personal-data-idle"><button type="button" class="button export-personal-data-handle"><?php _e( 'Email Data' ); ?></button></span> |
|---|
| 1455 | <span style="display:none" class="export-personal-data-processing button updating-message" ><?php _e( 'Sending Email...' ); ?></span> |
|---|
| 1456 | <span style="display:none" class="export-personal-data-success success-message" ><?php _e( 'Email sent.' ); ?></span> |
|---|
| 1457 | <span style="display:none" class="export-personal-data-failed"><?php _e( 'Email could not be sent.' ); ?> <button type="button" class="button export-personal-data-handle"><?php _e( 'Retry' ); ?></button></span> |
|---|
| 1458 | <?php |
|---|
| 1459 | |
|---|
| 1460 | echo '</div>'; |
|---|
| 1461 | break; |
|---|
| 1462 | case 'request-failed': |
|---|
| 1463 | submit_button( __( 'Retry' ), 'secondary', 'privacy_action_email_retry[' . $item->ID . ']', false ); |
|---|
| 1464 | break; |
|---|
| 1465 | case 'request-completed': |
|---|
| 1466 | echo '<a href="' . esc_url( wp_nonce_url( add_query_arg( array( |
|---|
| 1467 | 'action' => 'delete', |
|---|
| 1468 | 'request_id' => array( $item->ID ), |
|---|
| 1469 | ), admin_url( 'tools.php?page=export_personal_data' ) ), 'bulk-privacy_requests' ) ) . '" class="button">' . esc_html__( 'Remove request' ) . '</a>'; |
|---|
| 1470 | break; |
|---|
| 1471 | } |
|---|
| 1472 | } |
|---|
| 1473 | } |
|---|
| 1474 | |
|---|
| 1475 | /** |
|---|
| 1476 | * WP_Privacy_Data_Removal_Requests_Table class. |
|---|
| 1477 | * |
|---|
| 1478 | * @since 4.9.6 |
|---|
| 1479 | */ |
|---|
| 1480 | class WP_Privacy_Data_Removal_Requests_Table extends WP_Privacy_Requests_Table { |
|---|
| 1481 | /** |
|---|
| 1482 | * Action name for the requests this table will work with. |
|---|
| 1483 | * |
|---|
| 1484 | * @since 4.9.6 |
|---|
| 1485 | * |
|---|
| 1486 | * @var string $request_type Name of action. |
|---|
| 1487 | */ |
|---|
| 1488 | protected $request_type = 'remove_personal_data'; |
|---|
| 1489 | |
|---|
| 1490 | /** |
|---|
| 1491 | * Post type for the requests. |
|---|
| 1492 | * |
|---|
| 1493 | * @since 4.9.6 |
|---|
| 1494 | * |
|---|
| 1495 | * @var string $post_type The post type. |
|---|
| 1496 | */ |
|---|
| 1497 | protected $post_type = 'user_request'; |
|---|
| 1498 | |
|---|
| 1499 | /** |
|---|
| 1500 | * Actions column. |
|---|
| 1501 | * |
|---|
| 1502 | * @since 4.9.6 |
|---|
| 1503 | * |
|---|
| 1504 | * @param WP_User_Request $item Item being shown. |
|---|
| 1505 | * @return string Email column markup. |
|---|
| 1506 | */ |
|---|
| 1507 | public function column_email( $item ) { |
|---|
| 1508 | $row_actions = array(); |
|---|
| 1509 | |
|---|
| 1510 | // Allow the administrator to "force remove" the personal data even if confirmation has not yet been received. |
|---|
| 1511 | $status = $item->status; |
|---|
| 1512 | if ( 'request-confirmed' !== $status ) { |
|---|
| 1513 | /** This filter is documented in wp-admin/includes/ajax-actions.php */ |
|---|
| 1514 | $erasers = apply_filters( 'wp_privacy_personal_data_erasers', array() ); |
|---|
| 1515 | $erasers_count = count( $erasers ); |
|---|
| 1516 | $request_id = $item->ID; |
|---|
| 1517 | $nonce = wp_create_nonce( 'wp-privacy-erase-personal-data-' . $request_id ); |
|---|
| 1518 | |
|---|
| 1519 | $remove_data_markup = '<div class="remove-personal-data force-remove-personal-data" ' . |
|---|
| 1520 | 'data-erasers-count="' . esc_attr( $erasers_count ) . '" ' . |
|---|
| 1521 | 'data-request-id="' . esc_attr( $request_id ) . '" ' . |
|---|
| 1522 | 'data-nonce="' . esc_attr( $nonce ) . |
|---|
| 1523 | '">'; |
|---|
| 1524 | |
|---|
| 1525 | $remove_data_markup .= '<span class="remove-personal-data-idle"><button type="button" class="button-link remove-personal-data-handle">' . __( 'Force Erase Personal Data' ) . '</button></span>' . |
|---|
| 1526 | '<span style="display:none" class="remove-personal-data-processing" >' . __( 'Erasing Data...' ) . '</span>' . |
|---|
| 1527 | '<span style="display:none" class="remove-personal-data-failed">' . __( 'Force Erase has failed.' ) . ' <button type="button" class="button-link remove-personal-data-handle">' . __( 'Retry' ) . '</button></span>'; |
|---|
| 1528 | |
|---|
| 1529 | $remove_data_markup .= '</div>'; |
|---|
| 1530 | |
|---|
| 1531 | $row_actions = array( |
|---|
| 1532 | 'remove-data' => $remove_data_markup, |
|---|
| 1533 | ); |
|---|
| 1534 | } |
|---|
| 1535 | |
|---|
| 1536 | return sprintf( '<a href="%1$s">%2$s</a> %3$s', esc_url( 'mailto:' . $item->email ), $item->email, $this->row_actions( $row_actions ) ); |
|---|
| 1537 | } |
|---|
| 1538 | |
|---|
| 1539 | /** |
|---|
| 1540 | * Next steps column. |
|---|
| 1541 | * |
|---|
| 1542 | * @since 4.9.6 |
|---|
| 1543 | * |
|---|
| 1544 | * @param WP_User_Request $item Item being shown. |
|---|
| 1545 | */ |
|---|
| 1546 | public function column_next_steps( $item ) { |
|---|
| 1547 | $status = $item->status; |
|---|
| 1548 | |
|---|
| 1549 | switch ( $status ) { |
|---|
| 1550 | case 'request-pending': |
|---|
| 1551 | esc_html_e( 'Waiting for confirmation' ); |
|---|
| 1552 | break; |
|---|
| 1553 | case 'request-confirmed': |
|---|
| 1554 | /** This filter is documented in wp-admin/includes/ajax-actions.php */ |
|---|
| 1555 | $erasers = apply_filters( 'wp_privacy_personal_data_erasers', array() ); |
|---|
| 1556 | $erasers_count = count( $erasers ); |
|---|
| 1557 | $request_id = $item->ID; |
|---|
| 1558 | $nonce = wp_create_nonce( 'wp-privacy-erase-personal-data-' . $request_id ); |
|---|
| 1559 | |
|---|
| 1560 | echo '<div class="remove-personal-data" ' . |
|---|
| 1561 | 'data-force-erase="1" ' . |
|---|
| 1562 | 'data-erasers-count="' . esc_attr( $erasers_count ) . '" ' . |
|---|
| 1563 | 'data-request-id="' . esc_attr( $request_id ) . '" ' . |
|---|
| 1564 | 'data-nonce="' . esc_attr( $nonce ) . |
|---|
| 1565 | '">'; |
|---|
| 1566 | |
|---|
| 1567 | ?> |
|---|
| 1568 | <span class="remove-personal-data-idle"><button type="button" class="button remove-personal-data-handle"><?php _e( 'Erase Personal Data' ); ?></button></span> |
|---|
| 1569 | <span style="display:none" class="remove-personal-data-processing button updating-message" ><?php _e( 'Erasing Data...' ); ?></span> |
|---|
| 1570 | <span style="display:none" class="remove-personal-data-failed"><?php _e( 'Erasing Data has failed.' ); ?> <button type="button" class="button remove-personal-data-handle"><?php _e( 'Retry' ); ?></button></span> |
|---|
| 1571 | <?php |
|---|
| 1572 | |
|---|
| 1573 | echo '</div>'; |
|---|
| 1574 | |
|---|
| 1575 | break; |
|---|
| 1576 | case 'request-failed': |
|---|
| 1577 | submit_button( __( 'Retry' ), 'secondary', 'privacy_action_email_retry[' . $item->ID . ']', false ); |
|---|
| 1578 | break; |
|---|
| 1579 | case 'request-completed': |
|---|
| 1580 | echo '<a href="' . esc_url( wp_nonce_url( add_query_arg( array( |
|---|
| 1581 | 'action' => 'delete', |
|---|
| 1582 | 'request_id' => array( $item->ID ), |
|---|
| 1583 | ), admin_url( 'tools.php?page=remove_personal_data' ) ), 'bulk-privacy_requests' ) ) . '" class="button">' . esc_html__( 'Remove request' ) . '</a>'; |
|---|
| 1584 | break; |
|---|
| 1585 | } |
|---|
| 1586 | } |
|---|
| 1587 | |
|---|
| 1588 | } |
|---|