diff --git a/src/wp-includes/class-wp-comment-type.php b/src/wp-includes/class-wp-comment-type.php new file mode 100644 index 0000000000000..6f88c5e7b499d --- /dev/null +++ b/src/wp-includes/class-wp-comment-type.php @@ -0,0 +1,243 @@ +name = $comment_type; + + $this->set_props( $args ); + } + + /** + * Sets comment type properties. + * + * See the register_comment_type() function for accepted arguments for `$args`. + * + * @since 7.1.0 + * + * @param array|string $args Array or string of arguments for registering a comment type. + */ + public function set_props( $args ) { + $args = wp_parse_args( $args ); + + /** + * Filters the arguments for registering a comment type. + * + * @since 7.1.0 + * + * @param array $args Array of arguments for registering a comment type. + * See the register_comment_type() function for accepted arguments. + * @param string $comment_type Comment type key. + */ + $args = apply_filters( 'register_comment_type_args', $args, $this->name ); + + $comment_type = $this->name; + + /** + * Filters the arguments for registering a specific comment type. + * + * The dynamic portion of the filter name, `$comment_type`, refers to the comment type key. + * + * Possible hook names include: + * + * - `register_comment_comment_type_args` + * - `register_pingback_comment_type_args` + * + * @since 7.1.0 + * + * @param array $args Array of arguments for registering a comment type. + * See the register_comment_type() function for accepted arguments. + * @param string $comment_type Comment type key. + */ + $args = apply_filters( "register_{$comment_type}_comment_type_args", $args, $this->name ); + + /* + * Note: 'label' is intentionally omitted from the defaults. Leaving the property + * unset (null) lets get_comment_type_labels() fall back to the default labels, the + * same way WP_Post_Type and WP_Taxonomy behave. A 'label' default of false would be + * treated as a provided value and overwrite the default name with false. + */ + $defaults = array( + 'labels' => array(), + 'description' => '', + 'public' => true, + 'internal' => false, + 'show_ui' => null, + '_builtin' => false, + ); + + $args = array_merge( $defaults, $args ); + + // If not set, default to the setting for 'public'. + if ( null === $args['show_ui'] ) { + $args['show_ui'] = $args['public']; + } + + $args['name'] = $this->name; + + foreach ( $args as $property_name => $property_value ) { + $this->$property_name = $property_value; + } + + $this->labels = get_comment_type_labels( $this ); + $this->label = $this->labels->name; + } + + /** + * Returns the default labels for comment types. + * + * @since 7.1.0 + * + * @return (string|null)[][] The default labels for comment types. + */ + public static function get_default_labels() { + if ( ! empty( self::$default_labels ) ) { + return self::$default_labels; + } + + self::$default_labels = array( + 'name' => array( _x( 'Comments', 'comment type general name' ), null ), + 'singular_name' => array( _x( 'Comment', 'comment type singular name' ), null ), + ); + + return self::$default_labels; + } + + /** + * Resets the cache for the default labels. + * + * @since 7.1.0 + */ + public static function reset_default_labels() { + self::$default_labels = array(); + } +} diff --git a/src/wp-includes/comment-template.php b/src/wp-includes/comment-template.php index 43bd68ff972a4..5dc0c76ac3b21 100644 --- a/src/wp-includes/comment-template.php +++ b/src/wp-includes/comment-template.php @@ -1185,6 +1185,9 @@ function get_comment_type( $comment_id = 0 ) { * @param string|false $pingback_text Optional. String to display for pingback type. Default false. */ function comment_type( $comment_text = false, $trackback_text = false, $pingback_text = false ) { + // Whether the caller supplied custom text for the default comment label. + $comment_text_overridden = ( false !== $comment_text ); + if ( false === $comment_text ) { $comment_text = _x( 'Comment', 'noun' ); } @@ -1203,7 +1206,18 @@ function comment_type( $comment_text = false, $trackback_text = false, $pingback echo $pingback_text; break; default: - echo $comment_text; + /* + * For a registered, non-built-in comment type, fall back to its singular label + * when the caller did not supply custom text. Built-in types and explicit + * overrides keep their existing output. + */ + $comment_type_object = $comment_text_overridden ? null : get_comment_type_object( $type ); + + if ( $comment_type_object && ! $comment_type_object->_builtin && isset( $comment_type_object->labels->singular_name ) ) { + echo esc_html( $comment_type_object->labels->singular_name ); + } else { + echo $comment_text; + } } } diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index b93908adc0519..e8b4f28fe8ed9 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -273,6 +273,302 @@ function get_comments( $args = '' ) { return $query->query( $args ); } +/** + * Creates the initial comment types when 'init' action is fired. + * + * See register_comment_type() for accepted arguments. + * + * @since 7.1.0 + */ +function create_initial_comment_types() { + WP_Comment_Type::reset_default_labels(); + + register_comment_type( + 'comment', + array( + 'label' => __( 'Comments' ), + 'labels' => array( + 'singular_name' => _x( 'Comment', 'noun' ), + ), + 'public' => true, + '_builtin' => true, + ) + ); + + register_comment_type( + 'pingback', + array( + 'label' => __( 'Pingbacks' ), + 'labels' => array( + 'singular_name' => __( 'Pingback' ), + ), + 'public' => true, + '_builtin' => true, + ) + ); + + register_comment_type( + 'trackback', + array( + 'label' => __( 'Trackbacks' ), + 'labels' => array( + 'singular_name' => __( 'Trackback' ), + ), + 'public' => true, + '_builtin' => true, + ) + ); + + register_comment_type( + 'note', + array( + 'label' => _x( 'Notes', 'comment type general name' ), + 'labels' => array( + 'singular_name' => _x( 'Note', 'comment type singular name' ), + ), + 'public' => false, + 'internal' => true, + '_builtin' => true, + ) + ); +} + +/** + * Registers a comment type. + * + * Note: Comment type registrations should not be hooked before the {@see 'init'} action. + * This is because comment type slugs need to be reserved as part of the upgrade routine + * and global variables need to be available for the comment type to register itself. + * + * Comment types are stored verbatim in the `comment_type` column of the comments table. + * Registration provides labels and metadata for a type; it does not constrain which values + * may be stored. + * + * @since 7.1.0 + * + * @global WP_Comment_Type[] $wp_comment_types List of comment types. + * + * @param string $comment_type Comment type key. Must not exceed 20 characters and may only + * contain lowercase alphanumeric characters, dashes, and underscores. + * See sanitize_key(). + * @param array|string $args { + * Optional. Array or string of arguments for registering a comment type. Default empty array. + * + * @type string $label Name of the comment type shown in the menu. Usually plural. + * Default is value of $labels['name']. + * @type string[] $labels An array of labels for this comment type. If not set, comment + * labels are inherited. See get_comment_type_labels() for a full + * list of supported labels. + * @type string $description A short descriptive summary of what the comment type is. + * Default empty. + * @type bool $public Whether the comment type is intended for use publicly either via + * the admin interface or by front-end users. Default true. + * @type bool $internal Whether the comment type is for internal use only and should be + * excluded from default public-facing contexts. Default false. + * @type bool $show_ui Whether to generate and allow a UI for managing this comment type + * in the admin. Default is value of $public. + * } + * @return WP_Comment_Type|WP_Error The registered comment type object on success, + * WP_Error object on failure. + */ +function register_comment_type( $comment_type, $args = array() ) { + global $wp_comment_types; + + if ( ! is_array( $wp_comment_types ) ) { + $wp_comment_types = array(); + } + + // Sanitize comment type name. + $comment_type = sanitize_key( $comment_type ); + + if ( empty( $comment_type ) || strlen( $comment_type ) > 20 ) { + _doing_it_wrong( __FUNCTION__, __( 'Comment type names must be between 1 and 20 characters in length.' ), '7.1.0' ); + return new WP_Error( 'comment_type_length_invalid', __( 'Comment type names must be between 1 and 20 characters in length.' ) ); + } + + $comment_type_object = new WP_Comment_Type( $comment_type, $args ); + + $wp_comment_types[ $comment_type ] = $comment_type_object; + + /** + * Fires after a comment type is registered. + * + * @since 7.1.0 + * + * @param string $comment_type Comment type key. + * @param WP_Comment_Type $comment_type_object Comment type object. + */ + do_action( 'registered_comment_type', $comment_type, $comment_type_object ); + + /** + * Fires after a specific comment type is registered. + * + * The dynamic portion of the filter name, `$comment_type`, refers to the comment type key. + * + * Possible hook names include: + * + * - `registered_comment_type_comment` + * - `registered_comment_type_pingback` + * + * @since 7.1.0 + * + * @param string $comment_type Comment type key. + * @param WP_Comment_Type $comment_type_object Comment type object. + */ + do_action( "registered_comment_type_{$comment_type}", $comment_type, $comment_type_object ); + + return $comment_type_object; +} + +/** + * Unregisters a comment type. + * + * Cannot be used to unregister built-in comment types. + * + * @since 7.1.0 + * + * @global WP_Comment_Type[] $wp_comment_types List of comment types. + * + * @param string $comment_type Comment type key. + * @return true|WP_Error True on success, WP_Error on failure or if the comment type doesn't exist. + */ +function unregister_comment_type( $comment_type ) { + global $wp_comment_types; + + if ( ! comment_type_exists( $comment_type ) ) { + return new WP_Error( 'invalid_comment_type', __( 'Invalid comment type.' ) ); + } + + $comment_type_object = get_comment_type_object( $comment_type ); + + // Do not allow unregistering built-in comment types. + if ( $comment_type_object->_builtin ) { + return new WP_Error( 'invalid_comment_type', __( 'Unregistering a built-in comment type is not allowed.' ) ); + } + + unset( $wp_comment_types[ $comment_type ] ); + + /** + * Fires after a comment type is unregistered. + * + * @since 7.1.0 + * + * @param string $comment_type Comment type key. + */ + do_action( 'unregistered_comment_type', $comment_type ); + + return true; +} + +/** + * Retrieves a comment type object by name. + * + * @since 7.1.0 + * + * @global WP_Comment_Type[] $wp_comment_types List of comment types. + * + * @param string $comment_type The name of a registered comment type. + * @return WP_Comment_Type|null WP_Comment_Type object if it exists, null otherwise. + */ +function get_comment_type_object( $comment_type ) { + global $wp_comment_types; + + if ( ! is_scalar( $comment_type ) || empty( $wp_comment_types[ $comment_type ] ) ) { + return null; + } + + return $wp_comment_types[ $comment_type ]; +} + +/** + * Retrieves a list of registered comment type names or objects. + * + * @since 7.1.0 + * + * @global WP_Comment_Type[] $wp_comment_types List of comment types. + * + * @param array|string $args Optional. An array of key => value arguments to match against + * the comment type objects. Default empty array. + * @param string $output Optional. The type of output to return. Either comment type 'names' + * or 'objects'. Default 'names'. + * @param string $operator Optional. The logical operation to perform. 'or' means only one + * element from the array needs to match; 'and' means all elements + * must match; 'not' means no elements may match. Default 'and'. + * @return string[]|WP_Comment_Type[] An array of comment type names or objects. + */ +function get_comment_types( $args = array(), $output = 'names', $operator = 'and' ) { + global $wp_comment_types; + + $field = ( 'names' === $output ) ? 'name' : false; + + return wp_filter_object_list( $wp_comment_types, $args, $operator, $field ); +} + +/** + * Determines whether a comment type is registered. + * + * @since 7.1.0 + * + * @param string $comment_type Comment type name. + * @return bool Whether the comment type is registered. + */ +function comment_type_exists( $comment_type ) { + return (bool) get_comment_type_object( $comment_type ); +} + +/** + * Builds an object with all comment type labels out of a comment type object. + * + * @since 7.1.0 + * + * @param WP_Comment_Type $comment_type_object Comment type object. + * @return object { + * Comment type labels object. + * + * @type string $name General name for the comment type, usually plural. The same and + * overridden by `$comment_type_object->label`. Default 'Comments'. + * @type string $singular_name Name for one object of this comment type. Default 'Comment'. + * @type string $menu_name Label for the menu name. Default is the same as `name`. + * } + */ +function get_comment_type_labels( $comment_type_object ) { + $nohier_vs_hier_defaults = WP_Comment_Type::get_default_labels(); + + $nohier_vs_hier_defaults['menu_name'] = $nohier_vs_hier_defaults['name']; + + $labels = _get_custom_object_labels( $comment_type_object, $nohier_vs_hier_defaults ); + + $comment_type = $comment_type_object->name; + + $default_labels = clone $labels; + + /** + * Filters the labels of a specific comment type. + * + * The dynamic portion of the hook name, `$comment_type`, refers to the comment type slug. + * + * Possible hook names include: + * + * - `comment_type_labels_comment` + * - `comment_type_labels_pingback` + * + * @since 7.1.0 + * + * @see get_comment_type_labels() for the full list of comment type labels. + * + * Labels are stored unescaped, mirroring the post type and taxonomy label + * contract; callers must escape them on output (for example with esc_html()). + * + * @param object $labels Object with labels for the comment type as member variables. + */ + $labels = apply_filters( "comment_type_labels_{$comment_type}", $labels ); + + // Ensure that the filtered labels contain all required default values. + $labels = (object) array_merge( (array) $default_labels, (array) $labels ); + + return $labels; +} + /** * Retrieves all of the WordPress supported comment statuses. * diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 5581828a10b61..966c69cca5cf9 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -532,6 +532,10 @@ add_action( 'split_shared_term', '_wp_check_split_nav_menu_terms', 10, 4 ); add_action( 'wp_split_shared_term_batch', '_wp_batch_split_terms' ); +// Comment types. +add_action( 'init', 'create_initial_comment_types', 0 ); // Highest priority. +add_action( 'change_locale', 'create_initial_comment_types' ); + // Comment type updates. add_action( 'admin_init', '_wp_check_for_scheduled_update_comment_type' ); add_action( 'wp_update_comment_type_batch', '_wp_batch_update_comment_type' ); diff --git a/src/wp-settings.php b/src/wp-settings.php index ef5c7784ee561..9ec2c3607271d 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -232,6 +232,7 @@ require ABSPATH . WPINC . '/comment.php'; require ABSPATH . WPINC . '/class-wp-comment.php'; require ABSPATH . WPINC . '/class-wp-comment-query.php'; +require ABSPATH . WPINC . '/class-wp-comment-type.php'; require ABSPATH . WPINC . '/class-walker-comment.php'; require ABSPATH . WPINC . '/comment-template.php'; require ABSPATH . WPINC . '/rewrite.php'; @@ -554,10 +555,11 @@ // Create common globals. require ABSPATH . WPINC . '/vars.php'; -// Make taxonomies and posts available to plugins and themes. +// Make taxonomies, posts, and comment types available to plugins and themes. // @plugin authors: warning: these get registered again on the init hook. create_initial_taxonomies(); create_initial_post_types(); +create_initial_comment_types(); wp_start_scraping_edited_file_errors(); diff --git a/tests/phpunit/tests/comment/commentType.php b/tests/phpunit/tests/comment/commentType.php new file mode 100644 index 0000000000000..0f0010001204b --- /dev/null +++ b/tests/phpunit/tests/comment/commentType.php @@ -0,0 +1,138 @@ +post->create(); + } + + public function tear_down() { + global $wp_comment_types; + + foreach ( array_keys( $wp_comment_types ) as $comment_type ) { + if ( ! $wp_comment_types[ $comment_type ]->_builtin ) { + unset( $wp_comment_types[ $comment_type ] ); + } + } + + parent::tear_down(); + } + + /** + * Returns the output of comment_type() for a comment of the given type. + * + * @param string $type Comment type stored on the comment. + * @param mixed ...$args Optional arguments passed through to comment_type(). + * @return string Captured output. + */ + private function get_comment_type_output( $type, ...$args ) { + $comment_id = self::factory()->comment->create( + array( + 'comment_post_ID' => self::$post_id, + 'comment_type' => $type, + ) + ); + + $GLOBALS['comment'] = get_comment( $comment_id ); + + ob_start(); + comment_type( ...$args ); + $output = ob_get_clean(); + + unset( $GLOBALS['comment'] ); + + return $output; + } + + /** + * @ticket 35214 + */ + public function test_built_in_types_output_is_unchanged() { + $this->assertSame( 'Comment', $this->get_comment_type_output( 'comment' ) ); + $this->assertSame( 'Trackback', $this->get_comment_type_output( 'trackback' ) ); + $this->assertSame( 'Pingback', $this->get_comment_type_output( 'pingback' ) ); + } + + /** + * @ticket 35214 + */ + public function test_custom_text_overrides_are_respected() { + $this->assertSame( 'C', $this->get_comment_type_output( 'comment', 'C', 'T', 'P' ) ); + $this->assertSame( 'T', $this->get_comment_type_output( 'trackback', 'C', 'T', 'P' ) ); + $this->assertSame( 'P', $this->get_comment_type_output( 'pingback', 'C', 'T', 'P' ) ); + } + + /** + * @ticket 35214 + */ + public function test_registered_custom_type_outputs_its_label() { + register_comment_type( + 'foo', + array( + 'labels' => array( + 'singular_name' => 'Foo', + ), + ) + ); + + $this->assertSame( 'Foo', $this->get_comment_type_output( 'foo' ) ); + } + + /** + * @ticket 35214 + */ + public function test_unregistered_custom_type_falls_back_to_default_label() { + $this->assertSame( _x( 'Comment', 'noun' ), $this->get_comment_type_output( 'bar' ) ); + } + + /** + * @ticket 35214 + */ + public function test_custom_text_override_wins_over_registered_label() { + register_comment_type( + 'foo', + array( + 'labels' => array( + 'singular_name' => 'Foo', + ), + ) + ); + + $this->assertSame( 'Custom', $this->get_comment_type_output( 'foo', 'Custom' ) ); + } + + /** + * The registered label is escaped on output to guard against HTML/script injection. + * + * @ticket 35214 + */ + public function test_registered_label_is_escaped_on_output() { + register_comment_type( + 'foo', + array( + 'labels' => array( + 'singular_name' => 'Foo', + ), + ) + ); + + $this->assertSame( + esc_html( 'Foo' ), + $this->get_comment_type_output( 'foo' ) + ); + } +} diff --git a/tests/phpunit/tests/comment/types.php b/tests/phpunit/tests/comment/types.php new file mode 100644 index 0000000000000..54f8e2aa9ad33 --- /dev/null +++ b/tests/phpunit/tests/comment/types.php @@ -0,0 +1,294 @@ +_builtin ) { + unset( $wp_comment_types[ $comment_type ] ); + } + } + + parent::tear_down(); + } + + /** + * @ticket 35214 + */ + public function test_register_comment_type() { + $this->assertNull( get_comment_type_object( 'foo' ) ); + + register_comment_type( 'foo' ); + + $cobj = get_comment_type_object( 'foo' ); + $this->assertInstanceOf( 'WP_Comment_Type', $cobj ); + $this->assertSame( 'foo', $cobj->name ); + + // Test some defaults. + $this->assertTrue( $cobj->public ); + $this->assertFalse( $cobj->internal ); + $this->assertFalse( $cobj->_builtin ); + } + + /** + * @ticket 35214 + */ + public function test_register_comment_type_without_labels_uses_default_labels() { + register_comment_type( 'foo' ); + + $cobj = get_comment_type_object( 'foo' ); + + $this->assertSame( 'Comments', $cobj->label ); + $this->assertSame( 'Comments', $cobj->labels->name ); + $this->assertSame( 'Comment', $cobj->labels->singular_name ); + } + + /** + * @ticket 35214 + */ + public function test_register_comment_type_return_value() { + $this->assertInstanceOf( 'WP_Comment_Type', register_comment_type( 'foo' ) ); + } + + /** + * @ticket 35214 + * + * @expectedIncorrectUsage register_comment_type + */ + public function test_register_comment_type_with_too_long_name() { + $this->assertInstanceOf( 'WP_Error', register_comment_type( 'comment_type_with_a_too_long_name' ) ); + } + + /** + * @ticket 35214 + * + * @expectedIncorrectUsage register_comment_type + */ + public function test_register_comment_type_with_empty_name() { + $this->assertInstanceOf( 'WP_Error', register_comment_type( '' ) ); + } + + /** + * @ticket 35214 + */ + public function test_register_comment_type_show_ui_should_default_to_value_of_public() { + register_comment_type( 'public_type', array( 'public' => true ) ); + $this->assertTrue( get_comment_type_object( 'public_type' )->show_ui ); + + register_comment_type( 'private_type', array( 'public' => false ) ); + $this->assertFalse( get_comment_type_object( 'private_type' )->show_ui ); + } + + /** + * @ticket 35214 + */ + public function test_built_in_comment_types_are_registered() { + $this->assertTrue( comment_type_exists( 'comment' ) ); + $this->assertTrue( comment_type_exists( 'pingback' ) ); + $this->assertTrue( comment_type_exists( 'trackback' ) ); + $this->assertTrue( comment_type_exists( 'note' ) ); + } + + /** + * @ticket 35214 + */ + public function test_built_in_note_type_is_internal_and_non_public() { + $note = get_comment_type_object( 'note' ); + + $this->assertTrue( $note->internal ); + $this->assertFalse( $note->public ); + } + + /** + * @ticket 35214 + */ + public function test_comment_type_exists() { + $this->assertFalse( comment_type_exists( 'foo' ) ); + + register_comment_type( 'foo' ); + + $this->assertTrue( comment_type_exists( 'foo' ) ); + } + + /** + * @ticket 35214 + */ + public function test_get_comment_types_names() { + register_comment_type( 'foo' ); + + $types = get_comment_types(); + + $this->assertContains( 'comment', $types ); + $this->assertContains( 'foo', $types ); + } + + /** + * @ticket 35214 + */ + public function test_get_comment_types_objects() { + register_comment_type( 'foo' ); + + $types = get_comment_types( array(), 'objects' ); + + $this->assertInstanceOf( 'WP_Comment_Type', $types['foo'] ); + } + + /** + * @ticket 35214 + */ + public function test_get_comment_types_filtered_by_property() { + register_comment_type( 'foo', array( 'public' => false ) ); + + $public = get_comment_types( array( 'public' => true ) ); + + $this->assertContains( 'comment', $public ); + $this->assertNotContains( 'foo', $public ); + $this->assertNotContains( 'note', $public ); + } + + /** + * @ticket 35214 + * + * @covers ::unregister_comment_type + */ + public function test_unregister_comment_type() { + register_comment_type( 'foo' ); + + $this->assertTrue( unregister_comment_type( 'foo' ) ); + $this->assertNull( get_comment_type_object( 'foo' ) ); + } + + /** + * @ticket 35214 + * + * @covers ::unregister_comment_type + */ + public function test_unregister_comment_type_unknown_returns_error() { + $this->assertWPError( unregister_comment_type( 'does_not_exist' ) ); + } + + /** + * @ticket 35214 + * + * @covers ::unregister_comment_type + */ + public function test_unregister_comment_type_twice_returns_error() { + register_comment_type( 'foo' ); + + $this->assertTrue( unregister_comment_type( 'foo' ) ); + $this->assertWPError( unregister_comment_type( 'foo' ) ); + } + + /** + * @ticket 35214 + * + * @covers ::unregister_comment_type + * + * @dataProvider data_built_in_comment_types + */ + public function test_unregister_built_in_comment_type_is_not_allowed( $comment_type ) { + $this->assertWPError( unregister_comment_type( $comment_type ) ); + $this->assertTrue( comment_type_exists( $comment_type ) ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_built_in_comment_types() { + return array( + array( 'comment' ), + array( 'pingback' ), + array( 'trackback' ), + array( 'note' ), + ); + } + + /** + * @ticket 35214 + */ + public function test_registered_comment_type_actions_fire() { + $action = new MockAction(); + $action_for_foo = new MockAction(); + + add_action( 'registered_comment_type', array( $action, 'action' ) ); + add_action( 'registered_comment_type_foo', array( $action_for_foo, 'action' ) ); + + register_comment_type( 'foo' ); + + $this->assertSame( 1, $action->get_call_count() ); + $this->assertSame( 1, $action_for_foo->get_call_count() ); + } + + /** + * @ticket 35214 + */ + public function test_unregistered_comment_type_action_fires() { + register_comment_type( 'foo' ); + + $action = new MockAction(); + add_action( 'unregistered_comment_type', array( $action, 'action' ) ); + + unregister_comment_type( 'foo' ); + + $this->assertSame( 1, $action->get_call_count() ); + } + + /** + * @ticket 35214 + */ + public function test_labels_are_built_from_args() { + register_comment_type( + 'foo', + array( + 'label' => 'Foos', + 'labels' => array( + 'singular_name' => 'Foo', + ), + ) + ); + + $cobj = get_comment_type_object( 'foo' ); + + $this->assertSame( 'Foos', $cobj->label ); + $this->assertSame( 'Foos', $cobj->labels->name ); + $this->assertSame( 'Foo', $cobj->labels->singular_name ); + } + + /** + * @ticket 35214 + */ + public function test_comment_type_labels_filter() { + add_filter( + 'comment_type_labels_foo', + static function ( $labels ) { + $labels->singular_name = 'Filtered Foo'; + return $labels; + } + ); + + register_comment_type( 'foo' ); + + $this->assertSame( 'Filtered Foo', get_comment_type_object( 'foo' )->labels->singular_name ); + } +} diff --git a/tests/phpunit/tests/comment/wpCommentType.php b/tests/phpunit/tests/comment/wpCommentType.php new file mode 100644 index 0000000000000..bf6d3c7df28dd --- /dev/null +++ b/tests/phpunit/tests/comment/wpCommentType.php @@ -0,0 +1,120 @@ +assertSame( 'foo', $comment_type->name ); + $this->assertTrue( $comment_type->public ); + $this->assertFalse( $comment_type->internal ); + $this->assertFalse( $comment_type->_builtin ); + $this->assertTrue( $comment_type->show_ui ); + $this->assertFalse( $comment_type->hierarchical ); + } + + /** + * @ticket 35214 + * + * @covers ::set_props + */ + public function test_set_props_overrides_defaults() { + $comment_type = new WP_Comment_Type( + 'foo', + array( + 'public' => false, + 'internal' => true, + 'description' => 'A test comment type.', + ) + ); + + $this->assertFalse( $comment_type->public ); + $this->assertTrue( $comment_type->internal ); + $this->assertSame( 'A test comment type.', $comment_type->description ); + // show_ui follows public when not explicitly set. + $this->assertFalse( $comment_type->show_ui ); + } + + /** + * @ticket 35214 + * + * @covers ::set_props + */ + public function test_register_comment_type_args_filter() { + $filter = static function ( $args ) { + $args['public'] = false; + return $args; + }; + + add_filter( 'register_comment_type_args', $filter ); + $comment_type = new WP_Comment_Type( 'foo' ); + remove_filter( 'register_comment_type_args', $filter ); + + $this->assertFalse( $comment_type->public ); + } + + /** + * @ticket 35214 + * + * @covers ::set_props + */ + public function test_register_specific_comment_type_args_filter() { + $filter = static function ( $args ) { + $args['description'] = 'Filtered description.'; + return $args; + }; + + add_filter( 'register_foo_comment_type_args', $filter ); + $comment_type = new WP_Comment_Type( 'foo' ); + $other_type = new WP_Comment_Type( 'bar' ); + remove_filter( 'register_foo_comment_type_args', $filter ); + + $this->assertSame( 'Filtered description.', $comment_type->description ); + $this->assertSame( '', $other_type->description ); + } + + /** + * @ticket 35214 + * + * @covers ::get_default_labels + * @covers ::reset_default_labels + */ + public function test_get_default_labels_returns_expected_defaults() { + WP_Comment_Type::reset_default_labels(); + + $labels = WP_Comment_Type::get_default_labels(); + + $this->assertSame( 'Comments', $labels['name'][0] ); + $this->assertSame( 'Comment', $labels['singular_name'][0] ); + } + + /** + * @ticket 35214 + * + * @covers ::get_default_labels + * @covers ::reset_default_labels + */ + public function test_reset_default_labels_clears_cache() { + // Prime the cache, then mutate the returned (by-value) array. + WP_Comment_Type::get_default_labels(); + + WP_Comment_Type::reset_default_labels(); + + // A fresh call rebuilds the defaults from translation functions. + $labels = WP_Comment_Type::get_default_labels(); + $this->assertSame( 'Comments', $labels['name'][0] ); + } +}