WordPress.org

Make WordPress Core

Ticket #33717: 337171.14.patch

File 337171.14.patch, 16.0 KB (added by imath, 12 months ago)
  • src/wp-admin/css/dashboard.css

    diff --git src/wp-admin/css/dashboard.css src/wp-admin/css/dashboard.css
    index 2411c79900..e76acdeaa5 100644
    body #dashboard-widgets .postbox form .submit { 
    821821        position: relative;
    822822}
    823823
     824#activity-widget #the-comment-list .comment-item .attention {
     825        display: none;
     826        color: #d54e21;
     827        font-size: 90%;
     828}
     829
    824830#activity-widget #the-comment-list .avatar {
    825831        position: absolute;
    826832        top: 12px;
    body #dashboard-widgets .postbox form .submit { 
    857863        width: 4px;
    858864}
    859865
     866#activity-widget #the-comment-list .unapproved .attention {
     867        display: block;
     868}
     869
    860870#activity-widget #the-comment-list .spam-undo-inside .avatar,
    861871#activity-widget #the-comment-list .trash-undo-inside .avatar {
    862872        position: relative;
  • src/wp-admin/css/list-tables.css

    diff --git src/wp-admin/css/list-tables.css src/wp-admin/css/list-tables.css
    index 1febe4e837..56334925a7 100644
     
    217217        background-color: #f5f5f5;
    218218}
    219219
     220#the-comment-list .comment .attention {
     221        display: none;
     222        color: #d54e21;
     223        font-size: 90%;
     224}
     225
    220226#the-comment-list .unapproved th,
    221227#the-comment-list .unapproved td {
    222228        background-color: #fef7f1;
     
    230236        margin-left: 4px;
    231237}
    232238
     239#the-comment-list .comment.unapproved .attention {
     240        display: block;
     241}
     242
    233243#the-comment-list .approve a {
    234244        color: #006505;
    235245}
  • src/wp-admin/includes/class-wp-comments-list-table.php

    diff --git src/wp-admin/includes/class-wp-comments-list-table.php src/wp-admin/includes/class-wp-comments-list-table.php
    index 563187ffe4..daf837aaa6 100644
    class WP_Comments_List_Table extends WP_List_Table { 
    882882                        }
    883883                }
    884884
     885                if ( 0 === (int) $comment->comment_approved && true === (bool) get_comment_meta( $comment->comment_ID, '_wp_comment_author_notification_optin', true ) ) {
     886                        /* translators: %s is the comment author name. */
     887                        echo '<p class="attention"><span class="dashicons dashicons-info"></span> ' . sprintf( esc_html__( '%s has opted in to receive a notification on comment’s approval.' ), get_comment_author( $comment ) ) . '</p>';
     888                }
     889
    885890                comment_text( $comment );
    886891
    887892                if ( $this->user_can ) {
  • src/wp-admin/includes/dashboard.php

    diff --git src/wp-admin/includes/dashboard.php src/wp-admin/includes/dashboard.php
    index 6855aa6c96..7042972aad 100644
    function _wp_dashboard_recent_comments_row( &$comment, $show_date = true ) { 
    827827                                }
    828828                                ?>
    829829                        </p>
    830 
    831830                                <?php
     831                                if ( 0 === (int) $comment->comment_approved && true === (bool) get_comment_meta( $comment->comment_ID, '_wp_comment_author_notification_optin', true ) ) :
     832                                        ?>
     833                                                <p class="attention">
     834                                                        <span class="dashicons dashicons-info"></span>
     835                                                        <?php
     836                                                        /* translators: %s is the comment author name. */
     837                                                        printf( esc_html__( '%s has opted in to receive a notification on comment’s approval.' ), get_comment_author( $comment ) );
     838                                                        ?>
     839                                                </p>
     840                                        <?php
     841                                        endif;
     842
    832843                        else :
    833844                                switch ( $comment->comment_type ) {
    834845                                        case 'pingback':
  • src/wp-comments-post.php

    diff --git src/wp-comments-post.php src/wp-comments-post.php
    index 06cbd460f4..3b1a8b44ca 100644
    require __DIR__ . '/wp-load.php'; 
    2222
    2323nocache_headers();
    2424
    25 $comment = wp_handle_comment_submission( wp_unslash( $_POST ) );
    26 if ( is_wp_error( $comment ) ) {
    27         $data = (int) $comment->get_error_data();
    28         if ( ! empty( $data ) ) {
     25if ( isset( $_POST['wp-comment-approved-notification-optin'], $_POST['comment_ID'], $_POST['moderation-hash'] ) ) {
     26        $comment = get_comment( $_POST['comment_ID'] );
     27
     28        if ( ! is_null( $comment->comment_ID ) && 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 {
    2931                wp_die(
    30                         '<p>' . $comment->get_error_message() . '</p>',
    31                         __( 'Comment Submission Failure' ),
     32                        '<p>' . __( 'Sorry we haven’t found your comment.' ) . '</p>',
     33                        __( 'Comment Notification opt-in failure' ),
    3234                        array(
    33                                 'response'  => $data,
    3435                                'back_link' => true,
    3536                        )
    3637                );
    37         } else {
    38                 exit;
    3938        }
    40 }
     39} else {
     40        $comment = wp_handle_comment_submission( wp_unslash( $_POST ) );
     41        if ( is_wp_error( $comment ) ) {
     42                $data = (int) $comment->get_error_data();
     43                if ( ! empty( $data ) ) {
     44                        wp_die(
     45                                '<p>' . $comment->get_error_message() . '</p>',
     46                                __( 'Comment Submission Failure' ),
     47                                array(
     48                                        'response'  => $data,
     49                                        'back_link' => true,
     50                                )
     51                        );
     52                } else {
     53                        exit;
     54                }
     55        }
    4156
    42 $user            = wp_get_current_user();
    43 $cookies_consent = ( isset( $_POST['wp-comment-cookies-consent'] ) );
     57        $user            = wp_get_current_user();
     58        $cookies_consent = ( isset( $_POST['wp-comment-cookies-consent'] ) );
    4459
    45 /**
    46  * Perform other actions when comment cookies are set.
    47  *
    48  * @since 3.4.0
    49  * @since 4.9.6 The `$cookies_consent` parameter was added.
    50  *
    51  * @param WP_Comment $comment         Comment object.
    52  * @param WP_User    $user            Comment author's user object. The user may not exist.
    53  * @param bool       $cookies_consent Comment author's consent to store cookies.
    54  */
    55 do_action( 'set_comment_cookies', $comment, $user, $cookies_consent );
     60        /**
     61         * Perform other actions when comment cookies are set.
     62         *
     63         * @since 3.4.0
     64         * @since 4.9.6 The `$cookies_consent` parameter was added.
     65         *
     66         * @param WP_Comment $comment         Comment object.
     67         * @param WP_User    $user            Comment author's user object. The user may not exist.
     68         * @param bool       $cookies_consent Comment author's consent to store cookies.
     69         */
     70        do_action( 'set_comment_cookies', $comment, $user, $cookies_consent );
     71}
    5672
    5773$location = empty( $_POST['redirect_to'] ) ? get_comment_link( $comment ) : $_POST['redirect_to'] . '#comment-' . $comment->comment_ID;
    5874
  • src/wp-includes/class-walker-comment.php

    diff --git src/wp-includes/class-walker-comment.php src/wp-includes/class-walker-comment.php
    index 9df2d20b3b..72f536118e 100644
    class Walker_Comment extends Walker { 
    275275                return $comment_text;
    276276        }
    277277
     278        /**
     279         * Outputs the awaiting moderation text.
     280         *
     281         * @since 5.7.0
     282         *
     283         * @param WP_Comment $comment Comment to display.
     284         */
     285        protected function awaiting_moderation_text( $comment ) {
     286                if ( 0 !== (int) $comment->comment_approved ) {
     287                        return;
     288                }
     289
     290                $commenter = wp_get_current_commenter();
     291                if ( $commenter['comment_author_email'] ) {
     292                        $moderation_note = __( 'Your comment is awaiting moderation.' );
     293                } else {
     294                        $moderation_note = __( 'Your comment is awaiting moderation. This is a preview, your comment will be visible after it has been approved.' );
     295                }
     296
     297                printf( '<em class="comment-awaiting-moderation">%s</em>', esc_html( $moderation_note ) );
     298        }
     299
     300        /**
     301         * Outputs the comment's approval notification form.
     302         *
     303         * @since 5.7.0
     304         *
     305         * @param WP_Comment $comment Comment to display.
     306         */
     307        protected function comment_approval_notification_form( $comment ) {
     308                if ( 0 !== (int) $comment->comment_approved || ! has_action( 'comment_unapproved_to_approved', 'wp_new_comment_notify_comment_author' ) ) {
     309                        return;
     310                }
     311
     312                if ( true === (bool) get_comment_meta( $comment->comment_ID, '_wp_comment_author_notification_optin', true ) ) :
     313                        ?>
     314                        <p class="wp-comment-approved-notification-optedin"><?php esc_html_e( 'You will receive an email when your comment has been approved.' ); ?></p>
     315                <?php else : ?>
     316                        <form action="<?php echo esc_url( site_url( '/wp-comments-post.php' ) ); ?>" method="post">
     317                                <p>
     318                                        <label for="wp-comment-approved-notification-optin">
     319                                                <input type="checkbox" id="wp-comment-approved-notification-optin" name="wp-comment-approved-notification-optin">
     320                                                <?php esc_html_e( 'I want to be notified by email when my comment is approved.' ); ?>
     321                                        </label>
     322                                </p>
     323                                <input type="hidden" name="comment_ID" value="<?php echo absint( $comment->comment_ID ); ?>">
     324                                <input type="hidden" name="moderation-hash" value="<?php echo wp_hash( $comment->comment_date_gmt ); ?>">
     325                                <input type="submit" class="button" value="<?php echo esc_html_x( 'Save', 'comment approved notification form' ); ?>">
     326                        </form>
     327                        <?php
     328                endif;
     329        }
     330
    278331        /**
    279332         * Outputs a single comment.
    280333         *
    class Walker_Comment extends Walker { 
    297350
    298351                $commenter          = wp_get_current_commenter();
    299352                $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                 }
    306353                ?>
    307354                <<?php echo $tag; ?> <?php comment_class( $this->has_children ? 'parent' : '', $comment ); ?> id="comment-<?php comment_ID(); ?>">
    308355                <?php if ( 'div' !== $args['style'] ) : ?>
    class Walker_Comment extends Walker { 
    328375                        );
    329376                        ?>
    330377                </div>
    331                 <?php if ( '0' == $comment->comment_approved ) : ?>
    332                 <em class="comment-awaiting-moderation"><?php echo $moderation_note; ?></em>
     378                <?php $this->awaiting_moderation_text( $comment ); ?>
    333379                <br />
    334                 <?php endif; ?>
    335380
    336381                <div class="comment-meta commentmetadata">
    337382                        <?php
    class Walker_Comment extends Walker { 
    362407                                )
    363408                        )
    364409                );
     410
     411                // Output the comment approval notification form if needed.
     412                $this->comment_approval_notification_form( $comment );
    365413                ?>
    366414
    367415                <?php
    class Walker_Comment extends Walker { 
    401449
    402450                $commenter          = wp_get_current_commenter();
    403451                $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                 }
    410452                ?>
    411453                <<?php echo $tag; ?> id="comment-<?php comment_ID(); ?>" <?php comment_class( $this->has_children ? 'parent' : '', $comment ); ?>>
    412454                        <article id="div-comment-<?php comment_ID(); ?>" class="comment-body">
    class Walker_Comment extends Walker { 
    450492                                                ?>
    451493                                        </div><!-- .comment-metadata -->
    452494
    453                                         <?php if ( '0' == $comment->comment_approved ) : ?>
    454                                         <em class="comment-awaiting-moderation"><?php echo $moderation_note; ?></em>
    455                                         <?php endif; ?>
     495                                        <?php $this->awaiting_moderation_text( $comment ); ?>
    456496                                </footer><!-- .comment-meta -->
    457497
    458498                                <div class="comment-content">
    459                                         <?php comment_text(); ?>
     499                                        <?php
     500                                        comment_text();
     501
     502                                        // Output the comment approval notification form if needed.
     503                                        $this->comment_approval_notification_form( $comment );
     504                                        ?>
    460505                                </div><!-- .comment-content -->
    461506
    462507                                <?php
  • src/wp-includes/comment.php

    diff --git src/wp-includes/comment.php src/wp-includes/comment.php
    index ac37540be0..a01e553b4a 100644
    function wp_new_comment_notify_postauthor( $comment_ID ) { 
    23482348        return wp_notify_postauthor( $comment_ID );
    23492349}
    23502350
     2351/**
     2352 * Notify a 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 successfully.
     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        $comment_author = get_user_by( 'email', $comment->comment_author_email );
     2371
     2372        if ( ! $post ) {
     2373                return false;
     2374        }
     2375
     2376        // The comment was left by the post author.
     2377        if ( $comment->user_id === $post->post_author || get_userdata( $post->post_author ) === $comment_author ) {
     2378                return false;
     2379        }
     2380
     2381        // Make sure the comment author can notified by email.
     2382        if ( empty( $comment->comment_author_email ) ) {
     2383                return false;
     2384        }
     2385
     2386        if ( true !== (bool) get_comment_meta( $comment->comment_ID, '_wp_comment_author_notification_optin', true ) ) {
     2387                return false;
     2388        }
     2389
     2390        /**
     2391         * The blogname option is escaped with esc_html when
     2392         * saved into the database, we need to reverse this for
     2393         * the plain text area of the email.
     2394         */
     2395        $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
     2396
     2397        /* translators: 1: blog name, 2: post title */
     2398        $subject = sprintf( __( '[%1$s] Your comment on "%2$s" has been approved' ), $blogname, $post->post_title );
     2399
     2400        if ( ! empty( $comment->comment_author ) ) {
     2401                /* translators: 1: comment author's name */
     2402                $notify_message = sprintf( __( 'Howdy %s,' ), $comment->comment_author ) . "\r\n\r\n";
     2403        } else {
     2404                $notify_message = __( 'Howdy,' ) . "\r\n\r\n";
     2405        }
     2406
     2407        /* translators: 1: post title */
     2408        $notify_message .= sprintf( __( 'Your comment on the post "%s" has been approved.' ), $post->post_title ) . "\r\n\r\n";
     2409        /* translators: 1: comment permalink */
     2410        $notify_message .= sprintf( __( 'View comment: %s' ), get_comment_link( $comment ) ) . "\r\n";
     2411
     2412        /**
     2413         * Filter the comment approval notification email text.
     2414         *
     2415         * @since 5.7.0
     2416         *
     2417         * @param string     $notify_message The comment notification email text.
     2418         * @param WP_Comment $comment        Comment object.
     2419         */
     2420        $notify_message = apply_filters( 'comment_approval_notification_text', $notify_message, $comment );
     2421
     2422        /**
     2423         * Filter the comment approval notification email subject.
     2424         *
     2425         * @since 5.7.0
     2426         *
     2427         * @param string     $subject The comment notification email subject.
     2428         * @param WP_Comment $comment Comment object.
     2429         */
     2430        $subject = apply_filters( 'comment_approval_notification_subject', $subject, $comment );
     2431
     2432        $sent = wp_mail( $comment->comment_author_email, wp_specialchars_decode( $subject ), $notify_message );
     2433
     2434        // Delete the opt-in now the notification has been sent.
     2435        delete_comment_meta( $comment->comment_ID, '_wp_comment_author_notification_optin' );
     2436
     2437        return $sent;
     2438}
     2439
    23512440/**
    23522441 * Sets the status of a comment.
    23532442 *
  • src/wp-includes/default-filters.php

    diff --git src/wp-includes/default-filters.php src/wp-includes/default-filters.php
    index 96ef7d82eb..fd1d02a0f3 100644
    add_action( 'comment_post', 'wp_new_comment_notify_postauthor' ); 
    458458add_action( 'after_password_reset', 'wp_password_change_notification' );
    459459add_action( 'register_new_user', 'wp_send_new_user_notifications' );
    460460add_action( 'edit_user_created_user', 'wp_send_new_user_notifications', 10, 2 );
     461add_action( 'comment_unapproved_to_approved', 'wp_new_comment_notify_comment_author' );
    461462
    462463// REST API actions.
    463464add_action( 'init', 'rest_api_init' );
  • tests/phpunit/tests/comment.php

    diff --git tests/phpunit/tests/comment.php tests/phpunit/tests/comment.php
    index c3013ad2c6..2bd1fd690e 100644
    class Tests_Comment extends WP_UnitTestCase { 
    554554                return $notify_message;
    555555        }
    556556
     557        /**
     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 user 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                $this->assertTrue( $sent );
     580
     581                $resent = wp_new_comment_notify_comment_author( $c );
     582                $this->assertFalse( $resent );
     583        }
     584
    557585        /**
    558586         * @ticket 12431
    559587         */