diff --git a/public_html/wp-content/plugins/pattern-directory/includes/pattern-validation.php b/public_html/wp-content/plugins/pattern-directory/includes/pattern-validation.php index 20a7c345..a0ab4f7f 100644 --- a/public_html/wp-content/plugins/pattern-directory/includes/pattern-validation.php +++ b/public_html/wp-content/plugins/pattern-directory/includes/pattern-validation.php @@ -687,10 +687,11 @@ function validate_title( $prepared_post, $request ) { ); } - if ( ! is_title_valid( $title ) ) { + $title_error = get_title_error( $title ); + if ( $title_error ) { return new \WP_Error( 'rest_pattern_invalid_title', - __( 'Pattern title is invalid. The pattern title should describe the pattern.', 'wporg-patterns' ), + $title_error, array( 'status' => 400 ) ); } @@ -1120,33 +1121,33 @@ function check_for_spam( $post ) { } /** - * Helper function to check for a valid pattern title. + * Explain why a pattern title is invalid, so the author knows what to change. * * @param string $title Pattern title. - * @return boolean + * @return string The reason the title is invalid, or an empty string if it's valid. */ -function is_title_valid( $title ) { +function get_title_error( $title ) { if ( strip_shortcodes( $title ) !== $title || wp_strip_all_tags( $title ) !== $title ) { - return false; + return __( 'Pattern titles cannot contain HTML or shortcodes.', 'wporg-patterns' ); } if ( content_has_block_directives( $title ) ) { - return false; + return __( 'Pattern titles cannot contain interactivity directives.', 'wporg-patterns' ); } - // Check title against a list of disallowed words. - // Note the space after `test ` to avoid matching "testimonial". - $disallow_list = array( 'test ', 'testing', 'my pattern', 'wordpress', 'example' ); - - if ( 'test' === strtolower( $title ) ) { - return false; - } + // Whole words only, so "Latest Posts" and "Testimonial" are fine. + $disallow_list = array( 'test', 'testing', 'my pattern', 'my patterns', 'wordpress', 'example' ); foreach ( $disallow_list as $disallowed ) { - if ( false !== stripos( $title, $disallowed ) ) { - return false; + $pattern = '/\b' . str_replace( ' ', '\s+', preg_quote( $disallowed, '/' ) ) . '\b/iu'; + if ( preg_match( $pattern, $title, $matches ) ) { + return sprintf( + /* translators: %s: The word from the title that isn't allowed, e.g. "test". */ + __( 'Pattern titles cannot include "%s". The title should describe the pattern.', 'wporg-patterns' ), + $matches[0] + ); } } - return true; + return ''; } diff --git a/public_html/wp-content/plugins/pattern-directory/tests/phpunit/pattern-title-validation-test.php b/public_html/wp-content/plugins/pattern-directory/tests/phpunit/pattern-title-validation-test.php index d1095243..94fbc860 100644 --- a/public_html/wp-content/plugins/pattern-directory/tests/phpunit/pattern-title-validation-test.php +++ b/public_html/wp-content/plugins/pattern-directory/tests/phpunit/pattern-title-validation-test.php @@ -84,6 +84,16 @@ public function data_valid_title() { array( array_merge( $defaults, array( 'title' => 'Testimonial' ) ), ), + // Disallowed words inside other words. + array( + array_merge( $defaults, array( 'title' => 'Latest Posts' ) ), + ), + array( + array_merge( $defaults, array( 'title' => 'Contest Banner' ) ), + ), + array( + array_merge( $defaults, array( 'title' => 'Examples Grid' ) ), + ), array( array( 'title' => '', @@ -178,6 +188,49 @@ public function data_invalid_title() { ); } + /** + * Test that the error says why the title is invalid. + * + * @dataProvider data_invalid_title_message + * + * @param string $title Submitted title. + * @param string $expected_message Expected error message. + */ + public function test_invalid_title_message( $title, $expected_message ) { + wp_set_current_user( self::$user ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/wporg-pattern/' . self::$pattern_id ); + $request->set_header( 'content-type', 'application/json' ); + $request->set_body( + wp_json_encode( + array( + 'title' => $title, + 'status' => 'publish', + 'content' => self::$valid_content, + ) + ) + ); + + $data = rest_do_request( $request )->get_data(); + $this->assertSame( 'rest_pattern_invalid_title', $data['code'] ); + $this->assertSame( $expected_message, $data['message'] ); + } + + /** + * Data provider for the invalid title messages. + * + * @return array + */ + public function data_invalid_title_message() { + return array( + array( 'Test Pattern', 'Pattern titles cannot include "Test". The title should describe the pattern.' ), + array( 'My Pattern', 'Pattern titles cannot include "My Pattern". The title should describe the pattern.' ), + array( 'My Patterns', 'Pattern titles cannot include "My Patterns". The title should describe the pattern.' ), + array( 'WordPress Header', 'Pattern titles cannot include "WordPress". The title should describe the pattern.' ), + array( 'Quote markup', 'Pattern titles cannot contain HTML or shortcodes.' ), + ); + } + /** * Test invalid pattern title: Published pattern, has empty title with no new title. */