WordPress.org

Make WordPress Core

Ticket #33717: 33717.16.diff

File 33717.16.diff, 15.5 KB (added by johnbillion, 9 months ago)
  • package-lock.json

     
    39223922                                                "tar-fs": "^2.0.0",
    39233923                                                "unbzip2-stream": "^1.3.3",
    39243924                                                "ws": "^7.2.3"
     3925                                        },
     3926                                        "dependencies": {
     3927                                                "devtools-protocol": {
     3928                                                        "version": "0.0.818844",
     3929                                                        "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.818844.tgz",
     3930                                                        "integrity": "sha512-AD1hi7iVJ8OD0aMLQU5VK0XH9LDlA1+BcPIgrAxPfaibx2DbWucuyOhc4oyQCbnvDDO68nN6/LcKfqTP343Jjg==",
     3931                                                        "dev": true
     3932                                                }
    39253933                                        }
    39263934                                },
    39273935                                "rimraf": {
     
    56315639                        "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
    56325640                        "dev": true
    56335641                },
    5634                 "browser-resolve": {
    5635                         "version": "1.11.3",
    5636                         "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz",
    5637                         "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==",
    5638                         "dev": true,
    5639                         "requires": {
    5640                                 "resolve": "1.1.7"
    5641                         },
    5642                         "dependencies": {
    5643                                 "resolve": {
    5644                                         "version": "1.1.7",
    5645                                         "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
    5646                                         "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
    5647                                         "dev": true
    5648                                 }
    5649                         }
    5650                 },
    56515642                "browserify-aes": {
    56525643                        "version": "1.2.0",
    5653                         "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
     5644                        "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
    56545645                        "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
    56555646                        "dev": true,
    56565647                        "requires": {
     
    79597950                        "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
    79607951                        "dev": true
    79617952                },
    7962                 "devtools-protocol": {
    7963                         "version": "0.0.818844",
    7964                         "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.818844.tgz",
    7965                         "integrity": "sha512-AD1hi7iVJ8OD0aMLQU5VK0XH9LDlA1+BcPIgrAxPfaibx2DbWucuyOhc4oyQCbnvDDO68nN6/LcKfqTP343Jjg==",
    7966                         "dev": true
    7967                 },
    79687953                "diff": {
    79697954                        "version": "4.0.2",
    79707955                        "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
  • src/wp-comments-post.php

     
    2222
    2323nocache_headers();
    2424
     25if ( isset( $_POST['wp-comment-approved-notification-optin'], $_POST['comment_ID'], $_POST['moderation-hash'] ) ) {
     26        $comment = get_comment( $_POST['comment_ID'] );
     27
     28        if ( $comment && hash_equals( $_POST['moderation-hash'], wp_hash( $comment->comment_date_gmt ) ) ) {
     29                update_comment_meta( $comment->comment_ID, '_wp_comment_author_notification_optin', true );
     30        } else {
     31                wp_die(
     32                        '<p>' . __( 'Invalid comment ID.' ) . '</p>',
     33                        __( 'Comment Notification Opt-in Failure' ),
     34                        array(
     35                                'response'  => 404,
     36                                'back_link' => true,
     37                        )
     38                );
     39        }
     40} else {
    2541$comment = wp_handle_comment_submission( wp_unslash( $_POST ) );
    2642if ( is_wp_error( $comment ) ) {
    2743        $data = (int) $comment->get_error_data();
     
    5369 * @param bool       $cookies_consent Comment author's consent to store cookies.
    5470 */
    5571do_action( 'set_comment_cookies', $comment, $user, $cookies_consent );
     72}
    5673
    5774$location = empty( $_POST['redirect_to'] ) ? get_comment_link( $comment ) : $_POST['redirect_to'] . '#comment-' . $comment->comment_ID;
    5875
  • src/wp-includes/class-walker-comment.php

     
    4141        );
    4242
    4343        /**
     44         * Backward compability checks.
     45         *
     46         * @since 5.7.0
     47         *
     48         * @var bool
     49         */
     50        protected $needs_comment_approval_notification_output = true;
     51
     52        /**
    4453         * Starts the list before the elements are added.
    4554         *
    4655         * @since 2.7.0
     
    255264        /**
    256265         * Filters the comment text.
    257266         *
    258          * Removes links from the pending comment's text if the commenter did not consent
    259          * to the comment cookies.
     267         *   - Removes links from the pending comment's text if the commenter did not consent
     268         *     to the comment cookies
     269         *   - Prepends the approval notification opt-in form or message to pending comments
    260270         *
    261271         * @since 5.4.2
     272         * @since 5.7.0 Adds backcompatibilty output if needed.
    262273         *
    263274         * @param string          $comment_text Text of the current comment.
    264275         * @param WP_Comment|null $comment      The comment object. Null if not found.
     
    272283                        $comment_text = wp_kses( $comment_text, array() );
    273284                }
    274285
     286                /*
     287                 * Checks if we need to output the comment approval notification/form.
     288                 */
     289                if ( $this->needs_comment_approval_notification_output ) {
     290                        $comment_text = $this->comment_approval_notification_form( $comment ) . "\n" . $comment_text;
     291                }
     292
    275293                return $comment_text;
    276294        }
    277295
    278296        /**
     297         * Outputs the awaiting moderation text.
     298         *
     299         * @since 5.7.0
     300         *
     301         * @param WP_Comment $comment Comment to display.
     302         */
     303        protected function awaiting_moderation_text( $comment ) {
     304                if ( '0' !== $comment->comment_approved ) {
     305                        return;
     306                }
     307
     308                $commenter = wp_get_current_commenter();
     309
     310                if ( $commenter['comment_author_email'] ) {
     311                        $moderation_note = __( 'Your comment is awaiting moderation.' );
     312                } else {
     313                        $moderation_note = __( 'Your comment is awaiting moderation. This is a preview, your comment will be visible after it has been approved.' );
     314                }
     315
     316                printf(
     317                        '<em class="comment-awaiting-moderation">%s</em>',
     318                        esc_html( $moderation_note )
     319                );
     320        }
     321
     322        /**
     323         * Gets the approval notification message for a comment, or the form for opting in.
     324         *
     325         * @since 5.7.0
     326         *
     327         * @param WP_Comment $comment Comment to display.
     328         * @return string HTML output.
     329         */
     330        protected function comment_approval_notification_form( $comment ) {
     331                $comment_approval_output = '';
     332
     333                if ( '0' === $comment->comment_approved && has_action( 'comment_unapproved_to_approved', 'wp_new_comment_notify_comment_author' ) ) {
     334                        if ( get_comment_meta( $comment->comment_ID, '_wp_comment_author_notification_optin', true ) ) {
     335                                $comment_approval_output = sprintf(
     336                                        '<p><em class="wp-comment-approved-notification-optedin">%s</em></p>',
     337                                        esc_html__( 'You will receive an email when your comment is approved.' )
     338                                );
     339                        } else {
     340                                $comment_approval_output = sprintf(
     341                                        '<form action="%1$s" method="post">
     342                                                <p>
     343                                                        <label for="wp-comment-approved-notification-optin">
     344                                                                <input type="checkbox" id="wp-comment-approved-notification-optin" name="wp-comment-approved-notification-optin">
     345                                                                %2$s
     346                                                        </label>
     347                                                </p>
     348                                                <input type="hidden" name="comment_ID" value="%3$s">
     349                                                <input type="hidden" name="moderation-hash" value="%4$s">
     350                                                <input type="submit" class="button" value="%5$s">
     351                                        </form>',
     352                                        esc_url( site_url( '/wp-comments-post.php' ) ),
     353                                        esc_html__( 'I want to be notified by email when my comment is approved.' ),
     354                                        absint( $comment->comment_ID ),
     355                                        wp_hash( $comment->comment_date_gmt ),
     356                                        esc_html_x( 'Save', 'comment approval notification form' )
     357                                );
     358                        }
     359                }
     360
     361                // Skip the backcompat output.
     362                $this->needs_comment_approval_notification_output = false;
     363
     364                // Return the approval notification form.
     365                return $comment_approval_output;
     366        }
     367
     368        /**
    279369         * Outputs a single comment.
    280370         *
    281371         * @since 3.6.0
     
    297387
    298388                $commenter          = wp_get_current_commenter();
    299389                $show_pending_links = isset( $commenter['comment_author'] ) && $commenter['comment_author'];
    300 
    301                 if ( $commenter['comment_author_email'] ) {
    302                         $moderation_note = __( 'Your comment is awaiting moderation.' );
    303                 } else {
    304                         $moderation_note = __( 'Your comment is awaiting moderation. This is a preview; your comment will be visible after it has been approved.' );
    305                 }
    306390                ?>
    307391                <<?php echo $tag; ?> <?php comment_class( $this->has_children ? 'parent' : '', $comment ); ?> id="comment-<?php comment_ID(); ?>">
    308392                <?php if ( 'div' !== $args['style'] ) : ?>
     
    328412                        );
    329413                        ?>
    330414                </div>
    331                 <?php if ( '0' == $comment->comment_approved ) : ?>
    332                 <em class="comment-awaiting-moderation"><?php echo $moderation_note; ?></em>
    333                 <br />
    334                 <?php endif; ?>
     415
     416                <?php
     417                // Output the comment moderation feedback if needed.
     418                $this->awaiting_moderation_text( $comment );
     419
     420                // Output the comment approval notification form if needed.
     421                echo $this->comment_approval_notification_form( $comment );
     422                ?>
    335423
    336424                <div class="comment-meta commentmetadata">
    337425                        <?php
     
    401489
    402490                $commenter          = wp_get_current_commenter();
    403491                $show_pending_links = ! empty( $commenter['comment_author'] );
    404 
    405                 if ( $commenter['comment_author_email'] ) {
    406                         $moderation_note = __( 'Your comment is awaiting moderation.' );
    407                 } else {
    408                         $moderation_note = __( 'Your comment is awaiting moderation. This is a preview; your comment will be visible after it has been approved.' );
    409                 }
    410492                ?>
    411493                <<?php echo $tag; ?> id="comment-<?php comment_ID(); ?>" <?php comment_class( $this->has_children ? 'parent' : '', $comment ); ?>>
    412494                        <article id="div-comment-<?php comment_ID(); ?>" class="comment-body">
     
    450532                                                ?>
    451533                                        </div><!-- .comment-metadata -->
    452534
    453                                         <?php if ( '0' == $comment->comment_approved ) : ?>
    454                                         <em class="comment-awaiting-moderation"><?php echo $moderation_note; ?></em>
    455                                         <?php endif; ?>
     535                                        <?php
     536                                        // Output the comment moderation feedback if needed.
     537                                        $this->awaiting_moderation_text( $comment );
     538
     539                                        // Output the comment approval notification form if needed.
     540                                        echo $this->comment_approval_notification_form( $comment );
     541                                        ?>
    456542                                </footer><!-- .comment-meta -->
    457543
    458544                                <div class="comment-content">
  • src/wp-includes/comment.php

     
    23492349}
    23502350
    23512351/**
     2352 * Notifies the comment author when their comment gets approved.
     2353 *
     2354 * This notification is only sent once when the comment status
     2355 * changes from unapproved to approved.
     2356 *
     2357 * @since 5.7.0
     2358 *
     2359 * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
     2360 * @return bool Whether the email was sent.
     2361 */
     2362function wp_new_comment_notify_comment_author( $comment_id ) {
     2363        $comment = get_comment( $comment_id );
     2364
     2365        if ( ! $comment ) {
     2366                return false;
     2367        }
     2368
     2369        $post = get_post( $comment->comment_post_ID );
     2370
     2371        if ( ! $post ) {
     2372                return false;
     2373        }
     2374
     2375        // Make sure the comment author can be notified by email.
     2376        if ( empty( $comment->comment_author_email ) ) {
     2377                return false;
     2378        }
     2379
     2380        if ( ! get_comment_meta( $comment->comment_ID, '_wp_comment_author_notification_optin', true ) ) {
     2381                return false;
     2382        }
     2383
     2384        /**
     2385         * The blogname option is escaped with esc_html when
     2386         * saved into the database, we need to reverse this for
     2387         * the plain text area of the email.
     2388         */
     2389        $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
     2390
     2391        $subject = sprintf(
     2392                /* translators: 1: blog name, 2: post title */
     2393                __( '[%1$s] Your comment on "%2$s" has been approved' ),
     2394                $blogname,
     2395                $post->post_title
     2396        );
     2397
     2398        if ( ! empty( $comment->comment_author ) ) {
     2399                $notify_message = sprintf(
     2400                        /* translators: 1: comment author's name */
     2401                        __( 'Howdy %s,' ),
     2402                        $comment->comment_author
     2403                ) . "\r\n\r\n";
     2404        } else {
     2405                $notify_message = __( 'Howdy,' ) . "\r\n\r\n";
     2406        }
     2407
     2408        $notify_message .= sprintf(
     2409                /* translators: 1: post title */
     2410                __( 'Your comment on "%s" has been approved.' ),
     2411                $post->post_title
     2412        ) . "\r\n\r\n";
     2413
     2414        $notify_message .= sprintf(
     2415                /* translators: 1: comment permalink */
     2416                __( 'View comment: %s' ),
     2417                get_comment_link( $comment )
     2418        ) . "\r\n";
     2419
     2420        $email = array(
     2421                'to'      => $comment->comment_author_email,
     2422                'subject' => $subject,
     2423                'message' => $notify_message,
     2424                'headers' => '',
     2425        );
     2426
     2427        /**
     2428         * Filters the contents of the email sent to notify a comment author that their comment was approved.
     2429         *
     2430         * Content should be formatted for transmission via wp_mail().
     2431         *
     2432         * @since 5.7.0
     2433         *
     2434         * @param array      $email   {
     2435         *     Used to build wp_mail().
     2436         *
     2437         *     @type string $to      The email address of the comment author.
     2438         *     @type string $subject The subject of the email.
     2439         *     @type string $message The content of the email.
     2440         *     @type string $headers Headers.
     2441         * }
     2442         * @param WP_Comment $comment Comment object.
     2443         */
     2444        $email = apply_filters( 'comment_approval_notification', $email, $comment );
     2445
     2446        $sent = wp_mail(
     2447                $email['to'],
     2448                wp_specialchars_decode( $email['subject'] ),
     2449                $email['message'],
     2450                $email['headers']
     2451        );
     2452
     2453        // Delete the opt-in now the notification has been sent.
     2454        delete_comment_meta( $comment->comment_ID, '_wp_comment_author_notification_optin' );
     2455
     2456        return $sent;
     2457}
     2458
     2459/**
    23522460 * Sets the status of a comment.
    23532461 *
    23542462 * The {@see 'wp_set_comment_status'} action is called after the comment is handled.
  • src/wp-includes/default-filters.php

     
    468468add_action( 'after_password_reset', 'wp_password_change_notification' );
    469469add_action( 'register_new_user', 'wp_send_new_user_notifications' );
    470470add_action( 'edit_user_created_user', 'wp_send_new_user_notifications', 10, 2 );
     471add_action( 'comment_unapproved_to_approved', 'wp_new_comment_notify_comment_author' );
    471472
    472473// REST API actions.
    473474add_action( 'init', 'rest_api_init' );
  • tests/phpunit/tests/comment.php

     
    555555        }
    556556
    557557        /**
     558         * @ticket 33717
     559         */
     560        public function test_wp_new_comment_notify_comment_author_once_only() {
     561                $c = self::factory()->comment->create(
     562                        array(
     563                                'comment_post_ID'      => self::$post_id,
     564                                'comment_approved'     => '0',
     565                                'comment_author_email' => 'foo@bar.mail',
     566                        )
     567                );
     568
     569                // The comment author subscribed to receive an email once comment is approved.
     570                update_comment_meta( $c, '_wp_comment_author_notification_optin', true );
     571
     572                // For the purpose of the test we are removing this hook to directly use the function to notify the comment author.
     573                remove_action( 'comment_unapproved_to_approved', 'wp_new_comment_notify_comment_author' );
     574
     575                // Approve the comment.
     576                wp_set_comment_status( $c, 'approve' );
     577
     578                $sent = wp_new_comment_notify_comment_author( $c );
     579                $resent = wp_new_comment_notify_comment_author( $c );
     580
     581                $this->assertTrue( $sent );
     582                $this->assertFalse( $resent );
     583        }
     584
     585        /**
     586         * @ticket 33717
     587         */
     588        public function test_wp_new_comment_notify_comment_author_has_not_opted_in() {
     589                $c = self::factory()->comment->create(
     590                        array(
     591                                'comment_post_ID'      => self::$post_id,
     592                                'comment_approved'     => '0',
     593                                'comment_author_email' => 'bat@man.mail',
     594                        )
     595                );
     596
     597                // For the purpose of the test we are removing this hook to directly use the function to notify the comment author.
     598                remove_action( 'comment_unapproved_to_approved', 'wp_new_comment_notify_comment_author' );
     599
     600                // Approve the comment.
     601                wp_set_comment_status( $c, 'approve' );
     602
     603                $sent = wp_new_comment_notify_comment_author( $c );
     604
     605                // The comment author hasn't subscribed to receive an email, no email should be sent.
     606                $this->assertFalse( $sent );
     607        }
     608
     609        /**
    558610         * @ticket 12431
    559611         */
    560612        public function test_wp_new_comment_with_meta() {