is_wp_error( $comment_id ) ) { return $comment_id; } if ( ! $comment_id ) { return new WP_Error( 'comment_save_error', __( 'Error: The comment could not be saved. Please try again later.' ), 500 ); } return get_comment( $comment_id ); } /** * Registers the personal data exporter for comments. * * @since 4.9.6 * * @param array[] $exporters An array of personal data exporters. * @return array[] An array of personal data exporters. */ function wp_register_comment_personal_data_exporter( $exporters ) { $exporters['wordpress-comments'] = array( 'exporter_friendly_name' => __( 'WordPress Comments' ), 'callback' => 'wp_comments_personal_data_exporter', ); return $exporters; } /** * Finds and exports personal data associated with an email address from the comments table. * * @since 4.9.6 * * @param string $email_address The comment author email address. * @param int $page Comment page number. * @return array { * An array of personal data. * * @type array[] $data An array of personal data arrays. * @type bool $done Whether the exporter is finished. * } */ function wp_comments_personal_data_exporter( $email_address, $page = 1 ) { // Limit us to 500 comments at a time to avoid timing out. $number = 500; $page = (int) $page; $data_to_export = array(); $comments = get_comments( array( 'author_email' => $email_address, 'number' => $number, 'paged' => $page, 'orderby' => 'comment_ID', 'order' => 'ASC', 'update_comment_meta_cache' => false, ) ); $comment_prop_to_export = array( 'comment_author' => __( 'Comment Author' ), 'comment_author_email' => __( 'Comment Author Email' ), 'comment_author_url' => __( 'Comment Author URL' ), 'comment_author_IP' => __( 'Comment Author IP' ), 'comment_agent' => __( 'Comment Author User Agent' ), 'comment_date' => __( 'Comment Date' ), 'comment_content' => __( 'Comment Content' ), 'comment_link' => __( 'Comment URL' ), ); foreach ( (array) $comments as $comment ) { $comment_data_to_export = array(); foreach ( $comment_prop_to_export as $key => $name ) { $value = ''; switch ( $key ) { case 'comment_author': case 'comment_author_email': case 'comment_author_url': case 'comment_author_IP': case 'comment_agent': case 'comment_date': $value = $comment->{$key}; break; case 'comment_content': $value = get_comment_text( $comment->comment_ID ); break; case 'comment_link': $value = get_comment_link( $comment->comment_ID ); $value = sprintf( '%s', esc_url( $value ), esc_html( $value ) ); break; } if ( ! empty( $value ) ) { $comment_data_to_export[] = array( 'name' => $name, 'value' => $value, ); } } $data_to_export[] = array( 'group_id' => 'comments', 'group_label' => __( 'Comments' ), 'group_description' => __( 'User’s comment data.' ), 'item_id' => "comment-{$comment->comment_ID}", 'data' => $comment_data_to_export, ); } $done = count( $comments ) < $number; return array( 'data' => $data_to_export, 'done' => $done, ); } /** * Registers the personal data eraser for comments. * * @since 4.9.6 * * @param array $erasers An array of personal data erasers. * @return array An array of personal data erasers. */ function wp_register_comment_personal_data_eraser( $erasers ) { $erasers['wordpress-comments'] = array( 'eraser_friendly_name' => __( 'WordPress Comments' ), 'callback' => 'wp_comments_personal_data_eraser', ); return $erasers; } /** * Erases personal data associated with an email address from the comments table. * * @since 4.9.6 * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $email_address The comment author email address. * @param int $page Comment page number. * @return array { * Data removal results. * * @type bool $items_removed Whether items were actually removed. * @type bool $items_retained Whether items were retained. * @type string[] $messages An array of messages to add to the personal data export file. * @type bool $done Whether the eraser is finished. * } */ function wp_comments_personal_data_eraser( $email_address, $page = 1 ) { global $wpdb; if ( empty( $email_address ) ) { return array( 'items_removed' => false, 'items_retained' => false, 'messages' => array(), 'done' => true, ); } // Limit us to 500 comments at a time to avoid timing out. $number = 500; $page = (int) $page; $items_removed = false; $items_retained = false; $comments = get_comments( array( 'author_email' => $email_address, 'number' => $number, 'paged' => $page, 'orderby' => 'comment_ID', 'order' => 'ASC', 'include_unapproved' => true, ) ); /* translators: Name of a comment's author after being anonymized. */ $anon_author = __( 'Anonymous' ); $messages = array(); foreach ( (array) $comments as $comment ) { $anonymized_comment = array(); $anonymized_comment['comment_agent'] = ''; $anonymized_comment['comment_author'] = $anon_author; $anonymized_comment['comment_author_email'] = ''; $anonymized_comment['comment_author_IP'] = wp_privacy_anonymize_data( 'ip', $comment->comment_author_IP ); $anonymized_comment['comment_author_url'] = ''; $anonymized_comment['user_id'] = 0; $comment_id = (int) $comment->comment_ID; /** * Filters whether to anonymize the comment. * * @since 4.9.6 * * @param bool|string $anon_message Whether to apply the comment anonymization (bool) or a custom * message (string). Default true. * @param WP_Comment $comment WP_Comment object. * @param array $anonymized_comment Anonymized comment data. */ $anon_message = apply_filters( 'wp_anonymize_comment', true, $comment, $anonymized_comment ); if ( true !== $anon_message ) { if ( $anon_message && is_string( $anon_message ) ) { $messages[] = esc_html( $anon_message ); } else { /* translators: %d: Comment ID. */ $messages[] = sprintf( __( 'Comment %d contains personal data but could not be anonymized.' ), $comment_id ); } $items_retained = true; continue; } $args = array( 'comment_ID' => $comment_id, ); $updated = $wpdb->update( $wpdb->comments, $anonymized_comment, $args ); if ( $updated ) { $items_removed = true; clean_comment_cache( $comment_id ); } else { $items_retained = true; } } $done = count( $comments ) < $number; return array( 'items_removed' => $items_removed, 'items_retained' => $items_retained, 'messages' => $messages, 'done' => $done, ); } /** * Sets the last changed time for the 'comment' cache group. * * @since 5.0.0 */ function wp_cache_set_comments_last_changed() { wp_cache_set_last_changed( 'comment' ); } /** * Updates the comment type for a batch of comments. * * @since 5.5.0 * * @global wpdb $wpdb WordPress database abstraction object. */ function _wp_batch_update_comment_type() { global $wpdb; $lock_name = 'update_comment_type.lock'; // Try to lock. $lock_result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'no') /* LOCK */", $lock_name, time() ) ); if ( ! $lock_result ) { $lock_result = get_option( $lock_name ); // Bail if we were unable to create a lock, or if the existing lock is still valid. if ( ! $lock_result || ( $lock_result > ( time() - HOUR_IN_SECONDS ) ) ) { wp_schedule_single_event( time() + ( 5 * MINUTE_IN_SECONDS ), 'wp_update_comment_type_batch' ); return; } } // Update the lock, as by this point we've definitely got a lock, just need to fire the actions. update_option( $lock_name, time() ); // Check if there's still an empty comment type. $empty_comment_type = $wpdb->get_var( "SELECT comment_ID FROM $wpdb->comments WHERE comment_type = '' LIMIT 1" ); // No empty comment type, we're done here. if ( ! $empty_comment_type ) { update_option( 'finished_updating_comment_type', true ); delete_option( $lock_name ); return; } // Empty comment type found? We'll need to run this script again. wp_schedule_single_event( time() + ( 2 * MINUTE_IN_SECONDS ), 'wp_update_comment_type_batch' ); /** * Filters the comment batch size for updating the comment type. * * @since 5.5.0 * * @param int $comment_batch_size The comment batch size. Default 100. */ $comment_batch_size = (int) apply_filters( 'wp_update_comment_type_batch_size', 100 ); // Get the IDs of the comments to update. $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM {$wpdb->comments} WHERE comment_type = '' ORDER BY comment_ID DESC LIMIT %d", $comment_batch_size ) ); if ( $comment_ids ) { $comment_id_list = implode( ',', $comment_ids ); // Update the `comment_type` field value to be `comment` for the next batch of comments. $wpdb->query( "UPDATE {$wpdb->comments} SET comment_type = 'comment' WHERE comment_type = '' AND comment_ID IN ({$comment_id_list})" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared ); // Make sure to clean the comment cache. clean_comment_cache( $comment_ids ); } delete_option( $lock_name ); } /** * In order to avoid the _wp_batch_update_comment_type() job being accidentally removed, * check that it's still scheduled while we haven't finished updating comment types. * * @ignore * @since 5.5.0 */ function _wp_check_for_scheduled_update_comment_type() { if ( ! get_option( 'finished_updating_comment_type' ) && ! wp_next_scheduled( 'wp_update_comment_type_batch' ) ) { wp_schedule_single_event( time() + MINUTE_IN_SECONDS, 'wp_update_comment_type_batch' ); } }