Make WordPress Core

Changeset 60987


Ignore:
Timestamp:
10/20/2025 07:20:17 PM (4 months ago)
Author:
adamsilverstein
Message:

Editor: Introduce the PHP-related code for Notes.

Bring the PHP part of the new Notes feature into core for the 6.9 release. See related Gutenberg Issue: https://github.com/WordPress/gutenberg/issues/71826. These changes do not impact any user facing functionality, they simply prepare core for the JavaScript functionality that will come over in a separate sync.

Overview of changes:

  • Ensure Notes are not included in comment counts
  • Enable the note type (REST API)
  • Adjust capabilities so edit_post cap implies ability to edit notes
  • Enable empty and duplicate notes for resolve/re-open actions
  • Add control over notes with post type supports check
  • Register new note resolution status meta

Props: ristojovanovic, adamsilverstein, jeffpaul, wildworks, mamaduka, swissspidy, timothyblynjacobs, kadamwhite.
Fixes #64096.

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/class-wp-comments-list-table.php

    r59877 r60987  
    152152            'post_type'                 => $post_type,
    153153            'update_comment_post_cache' => true,
     154            'type__not_in'              => array( 'note' ),
    154155        );
    155156
  • trunk/src/wp-admin/includes/comment.php

    r60416 r60987  
    158158    $post_id_in    = "'" . implode( "', '", $post_id_array ) . "'";
    159159
    160     $pending = $wpdb->get_results( "SELECT comment_post_ID, COUNT(comment_ID) as num_comments FROM $wpdb->comments WHERE comment_post_ID IN ( $post_id_in ) AND comment_approved = '0' GROUP BY comment_post_ID", ARRAY_A );
     160    $pending = $wpdb->get_results( "SELECT comment_post_ID, COUNT(comment_ID) as num_comments FROM $wpdb->comments WHERE comment_post_ID IN ( $post_id_in ) AND comment_approved = '0' AND comment_type != 'note' GROUP BY comment_post_ID", ARRAY_A );
    161161
    162162    if ( $single ) {
  • trunk/src/wp-includes/comment.php

    r60406 r60987  
    418418        'update_comment_meta_cache' => false,
    419419        'orderby'                   => 'none',
     420        'type__not_in'              => array( 'note' ),
    420421    );
    421422    if ( $post_id > 0 ) {
     
    714715
    715716    $dupe_id = $wpdb->get_var( $dupe );
     717
     718    // Allow duplicate notes for resolution purposes.
     719    if ( isset( $commentdata['comment_type'] ) && 'note' === $commentdata['comment_type'] ) {
     720        $dupe_id = false;
     721    }
    716722
    717723    /**
     
    41044110    }
    41054111}
     4112
     4113/**
     4114 * Register initial note status meta.
     4115 *
     4116 * @since 6.9.0
     4117 */
     4118function wp_create_initial_comment_meta() {
     4119    register_meta(
     4120        'comment',
     4121        '_wp_note_status',
     4122        array(
     4123            'type'          => 'string',
     4124            'description'   => __( 'Note resolution status' ),
     4125            'single'        => true,
     4126            'show_in_rest'  => array(
     4127                'schema' => array(
     4128                    'type' => 'string',
     4129                    'enum' => array( 'resolved', 'reopen' ),
     4130                ),
     4131            ),
     4132        )
     4133    );
     4134}
     4135add_action( 'init', 'wp_create_initial_comment_meta' );
  • trunk/src/wp-includes/link-template.php

    r60900 r60987  
    43444344     * @since 3.0.0
    43454345     *
    4346      * @param array $types An array of content types. Default only contains 'comment'.
    4347      */
    4348     $allowed_comment_types = apply_filters( 'get_avatar_comment_types', array( 'comment' ) );
     4346     * @since 6.9.0 The 'note' comment type was added.
     4347     *
     4348     * @param array $types An array of content types. Default contains 'comment' and 'note'.
     4349     */
     4350    $allowed_comment_types = apply_filters( 'get_avatar_comment_types', array( 'comment', 'note' ) );
    43494351
    43504352    return in_array( $comment_type, (array) $allowed_comment_types, true );
  • trunk/src/wp-includes/post.php

    r60906 r60987  
    3838            'query_var'             => false,
    3939            'delete_with_user'      => true,
    40             'supports'              => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions', 'post-formats' ),
     40            'supports'              => array(
     41                'title',
     42                'editor' => array( 'notes' => true ),
     43                'author',
     44                'thumbnail',
     45                'excerpt',
     46                'trackbacks',
     47                'custom-fields',
     48                'comments',
     49                'revisions',
     50                'post-formats',
     51            ),
    4152            'show_in_rest'          => true,
    4253            'rest_base'             => 'posts',
     
    6374            'query_var'             => false,
    6475            'delete_with_user'      => true,
    65             'supports'              => array( 'title', 'editor', 'author', 'thumbnail', 'page-attributes', 'custom-fields', 'comments', 'revisions' ),
     76            'supports'              => array(
     77                'title',
     78                'editor' => array( 'notes' => true ),
     79                'author',
     80                'thumbnail',
     81                'page-attributes',
     82                'custom-fields',
     83                'comments',
     84                'revisions',
     85            ),
    6686            'show_in_rest'          => true,
    6787            'rest_base'             => 'pages',
     
    23302350    return ( isset( $_wp_post_type_features[ $post_type ][ $feature ] ) );
    23312351}
    2332 
    23332352/**
    23342353 * Retrieves a list of post type names that support a specific feature.
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php

    r60937 r60987  
    124124     */
    125125    public function get_items_permissions_check( $request ) {
     126        $is_note         = 'note' === $request['type'];
     127        $is_edit_context = 'edit' === $request['context'];
    126128
    127129        if ( ! empty( $request['post'] ) ) {
    128130            foreach ( (array) $request['post'] as $post_id ) {
    129131                $post = get_post( $post_id );
     132
     133                if ( $post && $is_note && ! $this->check_post_type_supports_notes( $post->post_type ) ) {
     134                    return new WP_Error(
     135                        'rest_comment_not_supported_post_type',
     136                        __( 'Sorry, this post type does not support notes.' ),
     137                        array( 'status' => 403 )
     138                    );
     139                }
    130140
    131141                if ( ! empty( $post_id ) && $post && ! $this->check_read_post_permission( $post, $request ) ) {
     
    145155        }
    146156
    147         if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) {
     157        // Re-map edit context capabilities when requesting `note` for a post.
     158        if ( $is_edit_context && $is_note && ! empty( $request['post'] ) ) {
     159            foreach ( (array) $request['post'] as $post_id ) {
     160                if ( ! current_user_can( 'edit_post', $post_id ) ) {
     161                    return new WP_Error(
     162                        'rest_forbidden_context',
     163                        __( 'Sorry, you are not allowed to edit comments.' ),
     164                        array( 'status' => rest_authorization_required_code() )
     165                    );
     166                }
     167            }
     168        } elseif ( $is_edit_context && ! current_user_can( 'moderate_comments' ) ) {
    148169            return new WP_Error(
    149170                'rest_forbidden_context',
     
    395416        }
    396417
    397         if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) {
     418        // Re-map edit context capabilities when requesting `note` type.
     419        $edit_cap = 'note' === $comment->comment_type ? array( 'edit_comment', $comment->comment_ID ) : array( 'moderate_comments' );
     420        if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( ...$edit_cap ) ) {
    398421            return new WP_Error(
    399422                'rest_forbidden_context',
     
    453476     */
    454477    public function create_item_permissions_check( $request ) {
     478        $is_note = ! empty( $request['type'] ) && 'note' === $request['type'];
     479
     480        if ( ! is_user_logged_in() && $is_note ) {
     481            return new WP_Error(
     482                'rest_comment_login_required',
     483                __( 'Sorry, you must be logged in to comment.' ),
     484                array( 'status' => 401 )
     485            );
     486        }
     487
    455488        if ( ! is_user_logged_in() ) {
    456489            if ( get_option( 'comment_registration' ) ) {
     
    506539        }
    507540
    508         if ( isset( $request['status'] ) && ! current_user_can( 'moderate_comments' ) ) {
     541        $edit_cap = $is_note ? array( 'edit_post', (int) $request['post'] ) : array( 'moderate_comments' );
     542        if ( isset( $request['status'] ) && ! current_user_can( ...$edit_cap ) ) {
    509543            return new WP_Error(
    510544                'rest_comment_invalid_status',
     
    533567        }
    534568
    535         if ( 'draft' === $post->post_status ) {
     569        if ( $is_note && ! $this->check_post_type_supports_notes( $post->post_type ) ) {
     570            return new WP_Error(
     571                'rest_comment_not_supported_post_type',
     572                __( 'Sorry, this post type does not support notes.' ),
     573                array( 'status' => 403 )
     574            );
     575        }
     576
     577        if ( 'draft' === $post->post_status && ! $is_note ) {
    536578            return new WP_Error(
    537579                'rest_comment_draft_post',
     
    557599        }
    558600
    559         if ( ! comments_open( $post->ID ) ) {
     601        if ( ! comments_open( $post->ID ) && ! $is_note ) {
    560602            return new WP_Error(
    561603                'rest_comment_closed',
     
    585627        }
    586628
    587         // Do not allow comments to be created with a non-default type.
    588         if ( ! empty( $request['type'] ) && 'comment' !== $request['type'] ) {
     629        // Do not allow comments to be created with a non-core type.
     630        if ( ! empty( $request['type'] ) && ! in_array( $request['type'], array( 'comment', 'note' ), true ) ) {
    589631            return new WP_Error(
    590632                'rest_invalid_comment_type',
     
    599641        }
    600642
    601         $prepared_comment['comment_type'] = 'comment';
     643        $prepared_comment['comment_type'] = $request['type'];
    602644
    603645        if ( ! isset( $prepared_comment['comment_content'] ) ) {
    604646            $prepared_comment['comment_content'] = '';
     647        }
     648
     649        // Include note metadata into check_is_comment_content_allowed.
     650        if ( isset( $request['meta']['_wp_note_status'] ) ) {
     651            $prepared_comment['meta']['_wp_note_status'] = $request['meta']['_wp_note_status'];
    605652        }
    606653
     
    15201567                    'context'     => array( 'view', 'edit', 'embed' ),
    15211568                    'readonly'    => true,
     1569                    'default'     => 'comment',
    15221570                ),
    15231571            ),
     
    19261974        }
    19271975
     1976        // Allow empty notes only when resolution metadata is valid.
     1977        if (
     1978            isset( $check['comment_type'] ) &&
     1979            'note' === $check['comment_type'] &&
     1980            isset( $check['meta']['_wp_note_status'] ) &&
     1981            in_array( $check['meta']['_wp_note_status'], array( 'resolved', 'reopen' ), true )
     1982        ) {
     1983            return true;
     1984        }
     1985
    19281986        /*
    19291987         * Do not allow a comment to be created with missing or empty
     
    19321990        return '' !== $check['comment_content'];
    19331991    }
     1992
     1993    /**
     1994     * Check if post type supports notes.
     1995     *
     1996     * @param string $post_type Post type name.
     1997     * @return bool True if post type supports notes, false otherwise.
     1998     */
     1999    private function check_post_type_supports_notes( $post_type ) {
     2000        $supports = get_all_post_type_supports( $post_type );
     2001        if ( ! isset( $supports['editor'] ) ) {
     2002            return false;
     2003        }
     2004        if ( ! is_array( $supports['editor'] ) ) {
     2005            return false;
     2006        }
     2007        foreach ( $supports['editor'] as $item ) {
     2008            if ( ! empty( $item['notes'] ) ) {
     2009                return true;
     2010            }
     2011        }
     2012        return false;
     2013    }
    19342014}
  • trunk/tests/phpunit/tests/rest-api/rest-comments-controller.php

    r60937 r60987  
    1313    protected static $editor_id;
    1414    protected static $moderator_id;
     15    protected static $contributor_id;
    1516    protected static $subscriber_id;
    1617    protected static $author_id;
     18    protected static $user_ids = array();
    1719
    1820    protected static $post_id;
     
    2729    protected static $total_comments = 30;
    2830    protected static $per_page       = 50;
     31    protected static $num_notes      = 10;
    2932
    3033    protected $endpoint;
     
    4043        );
    4144
    42         self::$superadmin_id = $factory->user->create(
     45        self::$superadmin_id  = $factory->user->create(
    4346            array(
    4447                'role'       => 'administrator',
     
    4649            )
    4750        );
    48         self::$admin_id      = $factory->user->create(
     51        self::$admin_id       = $factory->user->create(
    4952            array(
    5053                'role' => 'administrator',
    5154            )
    5255        );
    53         self::$editor_id     = $factory->user->create(
     56        self::$editor_id      = $factory->user->create(
    5457            array(
    5558                'role' => 'editor',
    5659            )
    5760        );
    58         self::$moderator_id  = $factory->user->create(
     61        self::$moderator_id   = $factory->user->create(
    5962            array(
    6063                'role' => 'comment_moderator',
    6164            )
    6265        );
    63         self::$subscriber_id = $factory->user->create(
     66        self::$contributor_id = $factory->user->create(
     67            array(
     68                'role' => 'contributor',
     69            )
     70        );
     71        self::$subscriber_id  = $factory->user->create(
    6472            array(
    6573                'role' => 'subscriber',
    6674            )
    6775        );
    68         self::$author_id     = $factory->user->create(
     76        self::$author_id      = $factory->user->create(
    6977            array(
    7078                'role'         => 'author',
     
    112120                'user_id'          => self::$subscriber_id,
    113121            )
     122        );
     123
     124        self::$user_ids = array(
     125            'superadmin'    => self::$superadmin_id,
     126            'administrator' => self::$admin_id,
     127            'editor'        => self::$editor_id,
     128            'moderator'     => self::$moderator_id,
     129            'contributor'   => self::$contributor_id,
     130            'subscriber'    => self::$subscriber_id,
     131            'author'        => self::$author_id,
    114132        );
    115133
     
    132150        self::delete_user( self::$editor_id );
    133151        self::delete_user( self::$moderator_id );
     152        self::delete_user( self::$contributor_id );
    134153        self::delete_user( self::$subscriber_id );
    135154        self::delete_user( self::$author_id );
     
    36223641        );
    36233642    }
     3643
     3644    /**
     3645     * Create a test post with note.
     3646     *
     3647     * @param int $user_id Post author's user ID.
     3648     * @return int Post ID.
     3649     */
     3650    protected function create_test_post_with_note( $role ) {
     3651        $user_id = self::$user_ids[ $role ];
     3652        $post_id = self::factory()->post->create(
     3653            array(
     3654                'post_title'   => 'Test Post for Notes',
     3655                'post_content' => 'This is a test post to check note permissions.',
     3656                'post_status'  => 'contributor' === $role ? 'draft' : 'publish',
     3657                'post_author'  => $user_id,
     3658            )
     3659        );
     3660
     3661        for ( $i = 0; $i < self::$num_notes; $i++ ) {
     3662            self::factory()->comment->create(
     3663                array(
     3664                    'comment_post_ID'  => $post_id,
     3665                    'comment_type'     => 'note',
     3666                    'comment_approved' => 0 === $i % 2 ? 1 : 0,
     3667                )
     3668            );
     3669        }
     3670
     3671        return $post_id;
     3672    }
     3673
     3674    /**
     3675     * @ticket 64096
     3676     */
     3677    public function test_cannot_read_note_without_post_type_support() {
     3678        register_post_type(
     3679            'no-notes',
     3680            array(
     3681                'label'        => 'No Notes',
     3682                'supports'     => array( 'title', 'editor', 'author', 'comments' ),
     3683                'show_in_rest' => true,
     3684                'public'       => true,
     3685            )
     3686        );
     3687
     3688        create_initial_rest_routes();
     3689        wp_set_current_user( self::$admin_id );
     3690
     3691        $post_id = self::factory()->post->create( array( 'post_type' => 'no-notes' ) );
     3692        $request = new WP_REST_Request( 'GET', '/wp/v2/comments' );
     3693        $request->set_param( 'post', $post_id );
     3694        $request->set_param( 'type', 'note' );
     3695        $request->set_param( 'context', 'edit' );
     3696
     3697        $response = rest_get_server()->dispatch( $request );
     3698        $this->assertErrorResponse( 'rest_comment_not_supported_post_type', $response, 403 );
     3699
     3700        _unregister_post_type( 'no-notes' );
     3701    }
     3702
     3703    /**
     3704     * @ticket 64096
     3705     */
     3706    public function test_create_note_require_login() {
     3707        wp_set_current_user( 0 );
     3708
     3709        $post_id = self::factory()->post->create();
     3710        $request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
     3711        $request->set_param( 'post', $post_id );
     3712        $request->set_param( 'type', 'note' );
     3713        $response = rest_get_server()->dispatch( $request );
     3714
     3715        $this->assertErrorResponse( 'rest_comment_login_required', $response, 401 );
     3716    }
     3717
     3718    /**
     3719     * @ticket 64096
     3720     */
     3721    public function test_cannot_create_note_without_post_type_support() {
     3722        register_post_type(
     3723            'no-note',
     3724            array(
     3725                'label'        => 'No Notes',
     3726                'supports'     => array( 'title', 'editor', 'author', 'comments' ),
     3727                'show_in_rest' => true,
     3728                'public'       => true,
     3729            )
     3730        );
     3731
     3732        wp_set_current_user( self::$admin_id );
     3733        $post_id = self::factory()->post->create( array( 'post_type' => 'no-note' ) );
     3734        $params  = array(
     3735            'post'         => $post_id,
     3736            'author_name'  => 'Ishmael',
     3737            'author_email' => 'herman-melville@earthlink.net',
     3738            'author_url'   => 'https://en.wikipedia.org/wiki/Herman_Melville',
     3739            'content'      => 'Call me Ishmael.',
     3740            'author'       => self::$admin_id,
     3741            'type'         => 'note',
     3742        );
     3743
     3744        $request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
     3745        $request->add_header( 'Content-Type', 'application/json' );
     3746        $request->set_body( wp_json_encode( $params ) );
     3747        $response = rest_get_server()->dispatch( $request );
     3748        $this->assertErrorResponse( 'rest_comment_not_supported_post_type', $response, 403 );
     3749
     3750        _unregister_post_type( 'no-note' );
     3751    }
     3752
     3753    /**
     3754     * @ticket 64096
     3755     */
     3756    public function test_create_note_draft_post() {
     3757        wp_set_current_user( self::$editor_id );
     3758        $draft_id = self::factory()->post->create(
     3759            array(
     3760                'post_status' => 'draft',
     3761            )
     3762        );
     3763        $params   = array(
     3764            'post'         => $draft_id,
     3765            'author_name'  => 'Ishmael',
     3766            'author_email' => 'herman-melville@earthlink.net',
     3767            'author_url'   => 'https://en.wikipedia.org/wiki/Herman_Melville',
     3768            'content'      => 'Call me Ishmael.',
     3769            'author'       => self::$editor_id,
     3770            'type'         => 'note',
     3771        );
     3772
     3773        $request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
     3774        $request->add_header( 'Content-Type', 'application/json' );
     3775        $request->set_body( wp_json_encode( $params ) );
     3776
     3777        $response    = rest_get_server()->dispatch( $request );
     3778        $data        = $response->get_data();
     3779        $new_comment = get_comment( $data['id'] );
     3780        $this->assertSame( 'Call me Ishmael.', $new_comment->comment_content );
     3781        $this->assertSame( 'note', $new_comment->comment_type );
     3782    }
     3783
     3784    /**
     3785     * @ticket 64096
     3786     */
     3787    public function test_create_note_status() {
     3788        wp_set_current_user( self::$author_id );
     3789        $post_id = self::factory()->post->create( array( 'post_author' => self::$author_id ) );
     3790
     3791        $params = array(
     3792            'post'         => $post_id,
     3793            'author_name'  => 'Ishmael',
     3794            'author_email' => 'herman-melville@earthlink.net',
     3795            'author_url'   => 'https://en.wikipedia.org/wiki/Herman_Melville',
     3796            'content'      => 'Comic Book Guy',
     3797            'author'       => self::$author_id,
     3798            'type'         => 'note',
     3799            'status'       => 'hold',
     3800        );
     3801
     3802        $request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
     3803        $request->add_header( 'Content-Type', 'application/json' );
     3804        $request->set_body( wp_json_encode( $params ) );
     3805
     3806        $response    = rest_get_server()->dispatch( $request );
     3807        $data        = $response->get_data();
     3808        $new_comment = get_comment( $data['id'] );
     3809
     3810        $this->assertSame( '0', $new_comment->comment_approved );
     3811        $this->assertSame( 'note', $new_comment->comment_type );
     3812    }
     3813
     3814    /**
     3815     * @ticket 64096
     3816     */
     3817    public function test_cannot_create_with_non_valid_comment_type() {
     3818        wp_set_current_user( self::$admin_id );
     3819        $post_id = $this->factory->post->create();
     3820
     3821        $params = array(
     3822            'post'         => $post_id,
     3823            'author_name'  => 'Ishmael',
     3824            'author_email' => 'herman-melville@earthlink.net',
     3825            'author_url'   => 'https://en.wikipedia.org/wiki/Herman_Melville',
     3826            'content'      => 'Comic Book Guy',
     3827            'author'       => self::$admin_id,
     3828            'type'         => 'review',
     3829        );
     3830
     3831        $request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
     3832        $request->add_header( 'Content-Type', 'application/json' );
     3833        $request->set_body( wp_json_encode( $params ) );
     3834        $response = rest_get_server()->dispatch( $request );
     3835
     3836        $this->assertErrorResponse( 'rest_invalid_comment_type', $response, 400 );
     3837    }
     3838
     3839    /**
     3840     * @ticket 64096
     3841     */
     3842    public function test_create_assigns_default_type() {
     3843        wp_set_current_user( self::$editor_id );
     3844        $post_id = self::factory()->post->create();
     3845
     3846        $params = array(
     3847            'post'         => $post_id,
     3848            'author_name'  => 'Ishmael',
     3849            'author_email' => 'herman-melville@earthlink.net',
     3850            'author_url'   => 'https://en.wikipedia.org/wiki/Herman_Melville',
     3851            'content'      => 'Comic Book Guy',
     3852            'author'       => self::$editor_id,
     3853        );
     3854
     3855        $request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
     3856        $request->add_header( 'Content-Type', 'application/json' );
     3857        $request->set_body( wp_json_encode( $params ) );
     3858
     3859        $response    = rest_get_server()->dispatch( $request );
     3860        $data        = $response->get_data();
     3861        $new_comment = get_comment( $data['id'] );
     3862
     3863        $this->assertSame( 'comment', $new_comment->comment_type );
     3864    }
     3865
     3866    /**
     3867     * @dataProvider data_note_status_provider
     3868     * @ticket 64096
     3869     */
     3870    public function test_create_empty_note_with_resolution_meta( $status ) {
     3871        wp_set_current_user( self::$editor_id );
     3872        $post_id = self::factory()->post->create();
     3873        $params  = array(
     3874            'post'         => $post_id,
     3875            'author_name'  => 'Editor',
     3876            'author_email' => 'editor@example.com',
     3877            'author_url'   => 'https://example.com',
     3878            'author'       => self::$editor_id,
     3879            'type'         => 'note',
     3880            'content'      => '',
     3881            'meta'         => array(
     3882                '_wp_note_status' => $status,
     3883            ),
     3884        );
     3885        $request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
     3886        $request->add_header( 'Content-Type', 'application/json' );
     3887        $request->set_body( wp_json_encode( $params ) );
     3888
     3889        $response = rest_get_server()->dispatch( $request );
     3890        $this->assertSame( 201, $response->get_status() );
     3891    }
     3892
     3893    /**
     3894     * @ticket 64096
     3895     */
     3896    public function test_cannot_create_empty_note_without_resolution_meta() {
     3897        wp_set_current_user( self::$editor_id );
     3898        $post_id = self::factory()->post->create();
     3899        $params  = array(
     3900            'post'         => $post_id,
     3901            'author_name'  => 'Editor',
     3902            'author_email' => 'editor@example.com',
     3903            'author_url'   => 'https://example.com',
     3904            'author'       => self::$editor_id,
     3905            'type'         => 'note',
     3906            'content'      => '',
     3907        );
     3908        $request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
     3909        $request->add_header( 'Content-Type', 'application/json' );
     3910        $request->set_body( wp_json_encode( $params ) );
     3911        $response = rest_get_server()->dispatch( $request );
     3912        $this->assertErrorResponse( 'rest_comment_content_invalid', $response, 400 );
     3913    }
     3914
     3915    /**
     3916     * @ticket 64096
     3917     */
     3918    public function test_cannot_create_empty_note_with_invalid_resolution_meta() {
     3919        wp_set_current_user( self::$editor_id );
     3920        $post_id = self::factory()->post->create();
     3921        $params  = array(
     3922            'post'         => $post_id,
     3923            'author_name'  => 'Editor',
     3924            'author_email' => 'editor@example.com',
     3925            'author_url'   => 'https://example.com',
     3926            'author'       => self::$editor_id,
     3927            'type'         => 'note',
     3928            'content'      => '',
     3929            'meta'         => array(
     3930                '_wp_note_status' => 'invalid',
     3931            ),
     3932        );
     3933        $request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
     3934        $request->add_header( 'Content-Type', 'application/json' );
     3935        $request->set_body( wp_json_encode( $params ) );
     3936        $response = rest_get_server()->dispatch( $request );
     3937        $this->assertErrorResponse( 'rest_comment_content_invalid', $response, 400 );
     3938    }
     3939
     3940    /**
     3941     * @ticket 64096
     3942     */
     3943    public function test_create_duplicate_note() {
     3944        wp_set_current_user( self::$editor_id );
     3945        $post_id = self::factory()->post->create();
     3946
     3947        for ( $i = 0; $i < 2; $i++ ) {
     3948            $params  = array(
     3949                'post'         => $post_id,
     3950                'author_name'  => 'Editor',
     3951                'author_email' => 'editor@example.com',
     3952                'author_url'   => 'https://example.com',
     3953                'author'       => self::$editor_id,
     3954                'type'         => 'note',
     3955                'content'      => 'Doplicated comment',
     3956            );
     3957            $request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
     3958            $request->add_header( 'Content-Type', 'application/json' );
     3959            $request->set_body( wp_json_encode( $params ) );
     3960            $response = rest_get_server()->dispatch( $request );
     3961            $this->assertSame( 201, $response->get_status() );
     3962        }
     3963    }
     3964
     3965    /**
     3966     * @dataProvider data_note_get_items_permissions_data_provider
     3967     * @ticket 64096
     3968     */
     3969    public function test_note_get_items_permissions_edit_context( $role, $post_author_role, $can_read ) {
     3970        wp_set_current_user( self::$user_ids[ $role ] );
     3971        $post_id = $this->create_test_post_with_note( $post_author_role );
     3972
     3973        $request = new WP_REST_Request( 'GET', '/wp/v2/comments' );
     3974        $request->set_param( 'post', $post_id );
     3975        $request->set_param( 'type', 'note' );
     3976        $request->set_param( 'status', 'all' );
     3977        $request->set_param( 'per_page', 100 );
     3978        $request->set_param( 'context', 'edit' );
     3979        $response = rest_get_server()->dispatch( $request );
     3980
     3981        if ( $can_read ) {
     3982            $comments = $response->get_data();
     3983            $this->assertEquals( self::$num_notes, count( $comments ) );
     3984        } else {
     3985            $this->assertErrorResponse( 'rest_forbidden_context', $response, 403 );
     3986        }
     3987
     3988        wp_delete_post( $post_id, true );
     3989    }
     3990
     3991    /**
     3992     * @ticket 64096
     3993     */
     3994    public function test_note_get_items_permissions_mixed_post_authors() {
     3995        $author_post_id = $this->create_test_post_with_note( 'author' );
     3996        $editor_post_id = $this->create_test_post_with_note( 'editor' );
     3997
     3998        wp_set_current_user( self::$author_id );
     3999
     4000        $request = new WP_REST_Request( 'GET', '/wp/v2/comments' );
     4001        $request->set_param( 'post', array( $author_post_id, $editor_post_id ) );
     4002        $request->set_param( 'type', 'note' );
     4003        $request->set_param( 'status', 'all' );
     4004        $request->set_param( 'per_page', 100 );
     4005        $request->set_param( 'context', 'edit' );
     4006        $response = rest_get_server()->dispatch( $request );
     4007
     4008        $this->assertErrorResponse( 'rest_forbidden_context', $response, 403 );
     4009
     4010        wp_delete_post( $author_post_id, true );
     4011        wp_delete_post( $editor_post_id, true );
     4012    }
     4013
     4014    /**
     4015     * @dataProvider data_note_get_items_permissions_data_provider
     4016     * @ticket 64096
     4017     */
     4018    public function test_note_get_item_permissions_edit_context( $role, $post_author_role, $can_read ) {
     4019        wp_set_current_user( self::$user_ids[ $role ] );
     4020
     4021        $post_id = self::factory()->post->create(
     4022            array(
     4023                'post_title'   => 'Test Post for Block Comments',
     4024                'post_content' => 'This is a test post to check block comment permissions.',
     4025                'post_status'  => 'contributor' === $post_author_role ? 'draft' : 'publish',
     4026                'post_author'  => self::$user_ids[ $post_author_role ],
     4027            )
     4028        );
     4029
     4030        $comment_id = self::factory()->comment->create(
     4031            array(
     4032                'comment_post_ID'  => $post_id,
     4033                'comment_type'     => 'note',
     4034                // Test with unapproved comment, which is more restrictive.
     4035                'comment_approved' => 0,
     4036                'user_id'          => self::$user_ids[ $post_author_role ],
     4037            )
     4038        );
     4039
     4040        $request = new WP_REST_Request( 'GET', '/wp/v2/comments/' . $comment_id );
     4041        $request->set_param( 'context', 'edit' );
     4042        $response = rest_get_server()->dispatch( $request );
     4043
     4044        if ( $can_read ) {
     4045            $comment = $response->get_data();
     4046            $this->assertEquals( $comment_id, $comment['id'] );
     4047        } else {
     4048            $this->assertErrorResponse( 'rest_forbidden_context', $response, 403 );
     4049        }
     4050
     4051        wp_delete_post( $post_id, true );
     4052    }
     4053
     4054    public function data_note_get_items_permissions_data_provider() {
     4055        return array(
     4056            'Administrator can see note on other posts'  => array( 'administrator', 'author', true ),
     4057            'Editor can see note on other posts'         => array( 'editor', 'contributor', true ),
     4058            'Author cannot see note on other posts'      => array( 'author', 'editor', false ),
     4059            'Contributor cannot see note on other posts' => array( 'contributor', 'author', false ),
     4060            'Subscriber cannot see note'                 => array( 'subscriber', 'author', false ),
     4061            'Author can see note on own post'            => array( 'author', 'author', true ),
     4062            'Contributor can see note on own post'       => array( 'contributor', 'contributor', true ),
     4063        );
     4064    }
     4065
     4066    public function data_note_status_provider() {
     4067        return array(
     4068            'resolved' => array( 'resolved' ),
     4069            'reopen'   => array( 'reopen' ),
     4070        );
     4071    }
    36244072}
Note: See TracChangeset for help on using the changeset viewer.