From 8778e5cb608f539d494939dd1ec0f802346fcfba Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Thu, 29 Jul 2021 14:03:55 +0200 Subject: [PATCH 01/13] Try: Add simplified readme validator --- checks/class-readme-check.php | 240 +++++++++++++++++++++ checks/class-readme-parser.php | 369 +++++++++++++++++++++++++++++++++ 2 files changed, 609 insertions(+) create mode 100644 checks/class-readme-check.php create mode 100644 checks/class-readme-parser.php diff --git a/checks/class-readme-check.php b/checks/class-readme-check.php new file mode 100644 index 00000000..b8fc869e --- /dev/null +++ b/checks/class-readme-check.php @@ -0,0 +1,240 @@ + $contents ) { + $other_filenames[] = tc_filename( $path ); + if ( tc_filename( $path ) == 'readme.txt' || tc_filename( $path ) == 'readme.md' ) { + $readme = $contents; + break; + } + } + + // Publish an error if there is no readme file. + if ( ! in_array( 'readme.txt', $other_filenames, true ) && ! in_array( 'readme.md', $other_filenames, true )) { + $this->error[] = sprintf( + '%s: %s', + __( 'ERROR', 'theme-check' ), + __( 'The readme file is missing.', 'theme-check' ), + ); + $ret = false; + } else { + // Parse the content of the readme. + $readme = new Readme_Parser( $readme ); + + // Fatal errors: + if ( empty( $readme->name ) ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README ERROR', 'theme-check' ), + /* translators: 1: 'Theme Name' section title, 2: 'Theme Name' */ + sprintf( + __( 'Could not find a theme name in the readme. Theme name format looks like: %1$s. Please change %2$s to reflect the actual name of your theme.', 'theme-check' ), + '=== Theme Name ===', + 'Theme Name' + ) + ); + $ret = false; + } + + // Warnings. + if ( isset( $readme->warnings['requires_header_ignored'] ) ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README WARNING', 'theme-check' ), + /* translators: 1: theme header tag; 2: Example version 5.0. 3: Example version 4.9. */ + sprintf( + __( 'The %1$s field in the readme was ignored. This field should only contain a valid WordPress version such as %2$s or %3$s.', 'theme-check' ), + 'Requires at least', + '' . number_format( $latest_wordpress_version, 1 ) . '', + '' . number_format( $latest_wordpress_version - 0.1, 1 ) . '' + ) + ); + } elseif ( empty( $readme->requires ) ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README WARNING', 'theme-check' ), + sprintf( + /* translators: %s: plugin header tag */ + __( 'The %s field is missing from the readme.', 'theme-check' ), + 'Requires at least' + ) + ); + } + + if ( isset( $readme->warnings['tested_header_ignored'] ) ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README WARNING', 'theme-check' ), + sprintf( + /* translators: 1: theme header tag; 2: Example version 5.0. 3: Example version 5.1. */ + __( 'The %1$s field in the readme was ignored. This field should only contain a valid WordPress version such as %2$s or %3$s.', 'theme-check' ), + 'Tested up to', + '' . number_format( $latest_wordpress_version, 1 ) . '', + '' . number_format( $latest_wordpress_version + 0.1, 1 ) . '' + ) + ); + } elseif ( empty( $readme->tested ) ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README WARNING', 'theme-check' ), + sprintf( + /* translators: %s: plugin header tag */ + __( 'The %s field is missing from the readme file.', 'wporg-plugins' ), + 'Tested up to' + ) + ); + } + + if ( isset( $readme->warnings['requires_php_header_ignored'] ) ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README WARNING', 'theme-check' ), + sprintf( + /* translators: 1: plugin header tag; 2: Example version 5.2.4. 3: Example version 7.0. */ + __( 'The %1$s field in the readme was ignored. This field should only contain a PHP version such as %2$s or %3$s.', 'theme-check' ), + 'Requires PHP', + '5.2.4', + '7.0' + ) + ); + } else if ( empty( $readme->requires_php ) ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README WARNING', 'theme-check' ), + sprintf( + /* translators: %s: plugin header tag */ + __( 'The %s field is missing from the readme.', 'theme-check' ), + 'Requires PHP' + ) + ); + } + + if ( isset( $readme->warnings['contributor_ignored'] ) ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README WARNING', 'theme-check' ), + sprintf( + /* translators: %s: theme header tag */ + __( 'One or more contributors listed in the readme were ignored. The %s field should only contain WordPress.org usernames. Remember that usernames are case-sensitive.', 'theme-check' ), + 'Contributors' + ) + ); + } elseif ( ! count( $readme->contributors ) ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README WARNING', 'theme-check' ), + sprintf( + /* translators: %s: theme header tag */ + __( 'The %s field is missing from the readme.', 'theme-check' ), + 'Contributors' + ) + ); + } + + if ( empty( $readme->license ) ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README WARNING', 'theme-check' ), + sprintf( + /* translators: %s: theme header tag */ + __( 'The %s field is missing from the readme.', 'theme-check' ), + 'License' + ) + ); + } + + if ( empty( $readme->license_uri ) ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README WARNING', 'theme-check' ), + sprintf( + /* translators: %s: theme header tag */ + __( 'The %s field is missing from the readme.', 'theme-check' ), + 'License URI' + ) + ); + } + + // Info. + if ( empty( $readme->sections['description'] ) ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README INFO', 'theme-check' ), + sprintf( + /* translators: %s: section title */ + __( 'No %s section was found in the readme.', 'theme-check' ), + '== Description ==' + ) + ); + } + + if ( empty( $readme->sections['faq'] ) ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README INFO', 'theme-check' ), + sprintf( + /* translators: %s: section title */ + __( 'No %s section was found in the readme.', 'theme-check' ), + '== Frequently Asked Questions ==' + ) + ); + } + + if ( empty( $readme->sections['changelog'] ) ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README INFO', 'theme-check' ), + sprintf( + /* translators: %s: section title */ + __( 'No %s section was found in the readme.', 'theme-check' ), + '== Changelog ==' + ) + ); + } + + } + } + + /** + * Get error messages from the checks. + * + * @return array Error message. + */ + public function getError() { + return $this->error; + } +} + +$themechecks[] = new Readme_Check(); diff --git a/checks/class-readme-parser.php b/checks/class-readme-parser.php new file mode 100644 index 00000000..d8f70e16 --- /dev/null +++ b/checks/class-readme-parser.php @@ -0,0 +1,369 @@ + to + * + * @var array + */ + private $alias_sections = array( + 'frequently_asked_questions' => 'faq', + 'change_log' => 'changelog', + ); + + /** + * These are the valid header mappings for the header. + * + * @var array + */ + private $valid_headers = array( + 'tested' => 'tested', + 'tested up to' => 'tested', + 'requires' => 'requires', + 'requires at least' => 'requires', + 'requires php' => 'requires_php', + 'tags' => 'tags', + 'contributors' => 'contributors', + 'donate link' => 'donate_link', + 'license' => 'license', + 'license uri' => 'license_uri', + ); + + /** + * Parser constructor. + * + * @param string $string Contents of a readme to parse. + * + */ + public function __construct( $string ) { + $this->parse_readme_contents( $string ); + } + + /** + * @param string $contents The contents of the readme to parse. + * @return bool + */ + protected function parse_readme_contents( $contents ) { + if ( preg_match( '!!u', $contents ) ) { + $contents = preg_split( '!\R!u', $contents ); + } else { + $contents = preg_split( '!\R!', $contents ); // regex failed due to invalid UTF8 in $contents, see #2298 + } + $contents = array_map( array( $this, 'strip_newlines' ), $contents ); + + // Strip UTF8 BOM if present. + if ( 0 === strpos( $contents[0], "\xEF\xBB\xBF" ) ) { + $contents[0] = substr( $contents[0], 3 ); + } + + // Convert UTF-16 files. + if ( 0 === strpos( $contents[0], "\xFF\xFE" ) ) { + foreach ( $contents as $i => $line ) { + $contents[ $i ] = mb_convert_encoding( $line, 'UTF-8', 'UTF-16' ); + } + } + + $line = $this->get_first_nonwhitespace( $contents ); + $this->name = $this->sanitize_text( trim( $line, "#= \t\0\x0B" ) ); + + // Strip Github style header\n==== underlines. + if ( ! empty( $contents ) && '' === trim( $contents[0], '=-' ) ) { + array_shift( $contents ); + } + + // Handle readme's which do `=== Theme Name ===\nMy SuperAwesome Name\n...` + if ( 'theme name' == strtolower( $this->name ) ) { + $this->name = $line = $this->get_first_nonwhitespace( $contents ); + + // Ensure that the line read wasn't an actual header or description. + if ( strlen( $line ) > 50 || preg_match( '~^(' . implode( '|', array_keys( $this->valid_headers ) ) . ')\s*:~i', $line ) ) { + $this->name = false; + array_unshift( $contents, $line ); + } + } + + // Parse headers. + $headers = array(); + + $line = $this->get_first_nonwhitespace( $contents ); + do { + $value = null; + if ( false === strpos( $line, ':' ) ) { + + // Some themes have line-breaks within the headers. + if ( empty( $line ) ) { + break; + } else { + continue; + } + } + + $bits = explode( ':', trim( $line ), 2 ); + list( $key, $value ) = $bits; + $key = strtolower( trim( $key, " \t*-\r\n" ) ); + if ( isset( $this->valid_headers[ $key ] ) ) { + $headers[ $this->valid_headers[ $key ] ] = trim( $value ); + } + } while ( ( $line = array_shift( $contents ) ) !== null ); + array_unshift( $contents, $line ); + + if ( ! empty( $headers['requires'] ) ) { + $this->requires = $this->sanitize_requires_version( $headers['requires'] ); + } + if ( ! empty( $headers['tested'] ) ) { + $this->tested = $this->sanitize_tested_version( $headers['tested'] ); + } + if ( ! empty( $headers['requires_php'] ) ) { + $this->requires_php = $this->sanitize_requires_php( $headers['requires_php'] ); + } + if ( ! empty( $headers['contributors'] ) ) { + $this->contributors = explode( ',', $headers['contributors'] ); + $this->contributors = array_map( 'trim', $this->contributors ); + } + if ( ! empty( $headers['license'] ) ) { + // Handle the many cases of "License: GPLv2 - http://..." + if ( empty( $headers['license_uri'] ) && preg_match( '!(https?://\S+)!i', $headers['license'], $url ) ) { + $headers['license_uri'] = $url[1]; + $headers['license'] = trim( str_replace( $url[1], '', $headers['license'] ), " -*\t\n\r\n" ); + } + $this->license = $headers['license']; + } + if ( ! empty( $headers['license_uri'] ) ) { + $this->license_uri = $headers['license_uri']; + } + + return true; + } + + /** + * @access protected + * + * @param string $contents + * @return string + */ + protected function get_first_nonwhitespace( &$contents ) { + while ( ( $line = array_shift( $contents ) ) !== null ) { + $trimmed = trim( $line ); + if ( ! empty( $trimmed ) ) { + break; + } + } + + return $line; + } + + /** + * @access protected + * + * @param string $line + * @return string + */ + protected function strip_newlines( $line ) { + return rtrim( $line, "\r\n" ); + } + + /** + * @access protected + * + * @param string $text + * @return string + */ + protected function sanitize_text( $text ) { + // not fancy + $text = strip_tags( $text ); + $text = esc_html( $text ); + $text = trim( $text ); + + return $text; + } + + /** + * Sanitizes the Requires PHP header to ensure that it's a valid version header. + * + * @param string $version + * @return string The sanitized $version + */ + protected function sanitize_requires_php( $version ) { + $version = trim( $version ); + + // x.y or x.y.z + if ( $version && ! preg_match( '!^\d+(\.\d+){1,2}$!', $version ) ) { + $this->warnings['requires_php_header_ignored'] = true; + // Ignore the readme value. + $version = ''; + } + + return $version; + } + + /** + * Sanitizes the Tested header to ensure that it's a valid version header. + * + * @param string $version + * @return string The sanitized $version + */ + protected function sanitize_tested_version( $version ) { + $version = trim( $version ); + $latest_wordpress_version = '5.8'; + + if ( $version ) { + + // Handle the edge-case of 'WordPress 5.0' and 'WP 5.0' for historical purposes. + $strip_phrases = [ + 'WordPress', + 'WP', + ]; + $version = trim( str_ireplace( $strip_phrases, '', $version ) ); + + // Strip off any -alpha, -RC, -beta suffixes, as these complicate comparisons and are rarely used. + list( $version, ) = explode( '-', $version ); + + if ( + // x.y or x.y.z + ! preg_match( '!^\d+\.\d(\.\d+)?$!', $version ) || + // Allow themes to mark themselves as compatible with Stable+0.1 (trunk/master) but not higher + ( + $latest_wordpress_version && + version_compare( (float)$version, (float)$latest_wordpress_version+0.1, '>' ) + ) + ) { + $this->warnings['tested_header_ignored'] = true; + // Ignore the readme value. + $version = ''; + } + } + + return $version; + } + + /** + * Sanitizes the Requires at least header to ensure that it's a valid version header. + * + * @param string $version + * @return string The sanitized $version + */ + protected function sanitize_requires_version( $version ) { + $version = trim( $version ); + $latest_wordpress_version = '5.9'; + if ( $version ) { + + // Handle the edge-case of 'WordPress 5.0' and 'WP 5.0' for historical purposes. + $strip_phrases = [ + 'WordPress', + 'WP', + 'or higher', + 'and above', + '+', + ]; + $version = trim( str_ireplace( $strip_phrases, '', $version ) ); + + // Strip off any -alpha, -RC, -beta suffixes, as these complicate comparisons and are rarely used. + list( $version, ) = explode( '-', $version ); + + if ( + // x.y or x.y.z + ! preg_match( '!^\d+\.\d(\.\d+)?$!', $version ) || + // Allow themes to mark themselves as requireing Stable+0.1 (trunk/master) but not higher + $latest_wordpress_version && ( (float)$version > (float)$latest_wordpress_version+0.1 ) + ) { + $this->warnings['requires_header_ignored'] = true; + // Ignore the readme value. + $version = ''; + } + } + + return $version; + } + +} From 0ffd65231ba025f03e31b1574d56c31615999300 Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Fri, 30 Jul 2021 07:44:16 +0200 Subject: [PATCH 02/13] Fix CS issues --- checks/class-readme-check.php | 31 +++++++------- checks/class-readme-parser.php | 75 ++++++++++++++++++++++++---------- 2 files changed, 68 insertions(+), 38 deletions(-) diff --git a/checks/class-readme-check.php b/checks/class-readme-check.php index b8fc869e..febdce0b 100644 --- a/checks/class-readme-check.php +++ b/checks/class-readme-check.php @@ -18,7 +18,7 @@ class Readme_Check implements themecheck { * * @var string $latest_wordpress_version */ - protected $latest_wordpress_version = '5.9'; + protected $latest_wordpress_version = '5.8'; /** * Check that return true for good/okay/acceptable, false for bad/not-okay/unacceptable. @@ -42,18 +42,18 @@ public function check( $php_files, $css_files, $other_files ) { } // Publish an error if there is no readme file. - if ( ! in_array( 'readme.txt', $other_filenames, true ) && ! in_array( 'readme.md', $other_filenames, true )) { + if ( ! in_array( 'readme.txt', $other_filenames, true ) && ! in_array( 'readme.md', $other_filenames, true ) ) { $this->error[] = sprintf( '%s: %s', __( 'ERROR', 'theme-check' ), - __( 'The readme file is missing.', 'theme-check' ), + __( 'The readme file is missing.', 'theme-check' ) ); - $ret = false; + $ret = false; } else { // Parse the content of the readme. $readme = new Readme_Parser( $readme ); - // Fatal errors: + // Error. if ( empty( $readme->name ) ) { $this->error[] = sprintf( '%s: %s', @@ -67,13 +67,13 @@ public function check( $php_files, $css_files, $other_files ) { ); $ret = false; } - + // Warnings. if ( isset( $readme->warnings['requires_header_ignored'] ) ) { $this->error[] = sprintf( '%s: %s', __( 'README WARNING', 'theme-check' ), - /* translators: 1: theme header tag; 2: Example version 5.0. 3: Example version 4.9. */ + /* translators: 1: theme header tag; 2: Example version 5.0. 3: Example version 4.9. */ sprintf( __( 'The %1$s field in the readme was ignored. This field should only contain a valid WordPress version such as %2$s or %3$s.', 'theme-check' ), 'Requires at least', @@ -86,7 +86,7 @@ public function check( $php_files, $css_files, $other_files ) { '%s: %s', __( 'README WARNING', 'theme-check' ), sprintf( - /* translators: %s: plugin header tag */ + /* translators: %s: theme header tag */ __( 'The %s field is missing from the readme.', 'theme-check' ), 'Requires at least' ) @@ -108,12 +108,12 @@ public function check( $php_files, $css_files, $other_files ) { } elseif ( empty( $readme->tested ) ) { $this->error[] = sprintf( '%s: %s', - __( 'README WARNING', 'theme-check' ), - sprintf( - /* translators: %s: plugin header tag */ - __( 'The %s field is missing from the readme file.', 'wporg-plugins' ), - 'Tested up to' - ) + __( 'README WARNING', 'theme-check' ), + sprintf( + /* translators: %s: plugin header tag */ + __( 'The %s field is missing from the readme.', 'theme-check' ), + 'Tested up to' + ) ); } @@ -129,7 +129,7 @@ public function check( $php_files, $css_files, $other_files ) { '7.0' ) ); - } else if ( empty( $readme->requires_php ) ) { + } elseif ( empty( $readme->requires_php ) ) { $this->error[] = sprintf( '%s: %s', __( 'README WARNING', 'theme-check' ), @@ -223,7 +223,6 @@ public function check( $php_files, $css_files, $other_files ) { ) ); } - } } diff --git a/checks/class-readme-parser.php b/checks/class-readme-parser.php index d8f70e16..c6e57785 100644 --- a/checks/class-readme-parser.php +++ b/checks/class-readme-parser.php @@ -7,66 +7,92 @@ class Readme_Parser { /** + * Theme name + * * @var string */ public $name = ''; /** + * Theme tags + * * @var array */ public $tags = array(); /** + * Minimum WordPress version + * * @var string */ public $requires = ''; /** + * Tested up to + * * @var string */ public $tested = ''; /** + * Requires PHP + * * @var string */ public $requires_php = ''; /** + * Contributors, a WordPress.org username. + * * @var array */ public $contributors = array(); /** + * Donation link + * * @var string */ public $donate_link = ''; /** + * Readme short description + * * @var string */ public $short_description = ''; /** + * Theme license + * * @var string */ public $license = ''; /** + * Theme license URI + * * @var string */ public $license_uri = ''; /** + * Readme sections + * * @var array */ public $sections = array(); /** + * Theme upgrade notice + * * @var array */ public $upgrade_notice = array(); /** + * Theme FAQ + * * @var array */ public $faq = array(); @@ -124,13 +150,14 @@ class Readme_Parser { * Parser constructor. * * @param string $string Contents of a readme to parse. - * */ public function __construct( $string ) { $this->parse_readme_contents( $string ); } /** + * Parse and sanitize the readme + * * @param string $contents The contents of the readme to parse. * @return bool */ @@ -227,9 +254,11 @@ protected function parse_readme_contents( $contents ) { } /** + * Get the first non white space in the readme file content + * * @access protected * - * @param string $contents + * @param string $contents Readme file content. * @return string */ protected function get_first_nonwhitespace( &$contents ) { @@ -244,9 +273,11 @@ protected function get_first_nonwhitespace( &$contents ) { } /** + * Strip new lines + * * @access protected * - * @param string $line + * @param string $line The line to remove the line endings from. * @return string */ protected function strip_newlines( $line ) { @@ -254,13 +285,14 @@ protected function strip_newlines( $line ) { } /** + * Sanitize and remove characters from the theme name + * * @access protected * - * @param string $text + * @param string $text Text to sanitize. * @return string */ protected function sanitize_text( $text ) { - // not fancy $text = strip_tags( $text ); $text = esc_html( $text ); $text = trim( $text ); @@ -269,9 +301,9 @@ protected function sanitize_text( $text ) { } /** - * Sanitizes the Requires PHP header to ensure that it's a valid version header. + * Sanitizes the Requires PHP header to ensure that it's a valid version header * - * @param string $version + * @param string $version Minimum required PHP version. * @return string The sanitized $version */ protected function sanitize_requires_php( $version ) { @@ -288,23 +320,22 @@ protected function sanitize_requires_php( $version ) { } /** - * Sanitizes the Tested header to ensure that it's a valid version header. + * Sanitizes the Tested header to ensure that it's a valid version header * - * @param string $version + * @param string $version WordPress version that the theme is tested with. * @return string The sanitized $version */ protected function sanitize_tested_version( $version ) { - $version = trim( $version ); + $version = trim( $version ); $latest_wordpress_version = '5.8'; if ( $version ) { - // Handle the edge-case of 'WordPress 5.0' and 'WP 5.0' for historical purposes. $strip_phrases = [ 'WordPress', 'WP', ]; - $version = trim( str_ireplace( $strip_phrases, '', $version ) ); + $version = trim( str_ireplace( $strip_phrases, '', $version ) ); // Strip off any -alpha, -RC, -beta suffixes, as these complicate comparisons and are rarely used. list( $version, ) = explode( '-', $version ); @@ -315,9 +346,9 @@ protected function sanitize_tested_version( $version ) { // Allow themes to mark themselves as compatible with Stable+0.1 (trunk/master) but not higher ( $latest_wordpress_version && - version_compare( (float)$version, (float)$latest_wordpress_version+0.1, '>' ) + version_compare( (float) $version, (float) $latest_wordpress_version + 0.1, '>' ) ) - ) { + ) { $this->warnings['tested_header_ignored'] = true; // Ignore the readme value. $version = ''; @@ -328,16 +359,16 @@ protected function sanitize_tested_version( $version ) { } /** - * Sanitizes the Requires at least header to ensure that it's a valid version header. + * Sanitizes the Requires at least header to ensure that it's a valid version header * - * @param string $version + * @param string $version The minim required WordPress version. * @return string The sanitized $version */ protected function sanitize_requires_version( $version ) { - $version = trim( $version ); + $version = trim( $version ); $latest_wordpress_version = '5.9'; - if ( $version ) { + if ( $version ) { // Handle the edge-case of 'WordPress 5.0' and 'WP 5.0' for historical purposes. $strip_phrases = [ 'WordPress', @@ -346,8 +377,8 @@ protected function sanitize_requires_version( $version ) { 'and above', '+', ]; - $version = trim( str_ireplace( $strip_phrases, '', $version ) ); - + $version = trim( str_ireplace( $strip_phrases, '', $version ) ); + // Strip off any -alpha, -RC, -beta suffixes, as these complicate comparisons and are rarely used. list( $version, ) = explode( '-', $version ); @@ -355,8 +386,8 @@ protected function sanitize_requires_version( $version ) { // x.y or x.y.z ! preg_match( '!^\d+\.\d(\.\d+)?$!', $version ) || // Allow themes to mark themselves as requireing Stable+0.1 (trunk/master) but not higher - $latest_wordpress_version && ( (float)$version > (float)$latest_wordpress_version+0.1 ) - ) { + $latest_wordpress_version && ( (float) $version > (float) $latest_wordpress_version + 0.1 ) + ) { $this->warnings['requires_header_ignored'] = true; // Ignore the readme value. $version = ''; From 1353ae6d33de911423e4163b7554644e47c44a15 Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Fri, 30 Jul 2021 08:10:49 +0200 Subject: [PATCH 03/13] Replace short array syntax Replace short array syntax Remove the readme.txt file from class-file-check.php Update the error message when the file is missing. --- checks/class-file-check.php | 2 +- checks/class-readme-check.php | 2 +- checks/class-readme-parser.php | 15 +++------------ 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/checks/class-file-check.php b/checks/class-file-check.php index 90607752..f0fce6c1 100644 --- a/checks/class-file-check.php +++ b/checks/class-file-check.php @@ -66,7 +66,7 @@ public function check( $php_files, $css_files, $other_files ) { 'favicon\.ico' => __( 'Favicon', 'theme-check' ), ); - $musthave = array( 'index.php', 'style.css', 'readme.txt' ); + $musthave = array( 'index.php', 'style.css' ); checkcount(); diff --git a/checks/class-readme-check.php b/checks/class-readme-check.php index febdce0b..2c5013e0 100644 --- a/checks/class-readme-check.php +++ b/checks/class-readme-check.php @@ -45,7 +45,7 @@ public function check( $php_files, $css_files, $other_files ) { if ( ! in_array( 'readme.txt', $other_filenames, true ) && ! in_array( 'readme.md', $other_filenames, true ) ) { $this->error[] = sprintf( '%s: %s', - __( 'ERROR', 'theme-check' ), + __( 'REQUIRED', 'theme-check' ), __( 'The readme file is missing.', 'theme-check' ) ); $ret = false; diff --git a/checks/class-readme-parser.php b/checks/class-readme-parser.php index c6e57785..2f56bdb4 100644 --- a/checks/class-readme-parser.php +++ b/checks/class-readme-parser.php @@ -293,7 +293,7 @@ protected function strip_newlines( $line ) { * @return string */ protected function sanitize_text( $text ) { - $text = strip_tags( $text ); + $text = wp_strip_all_tags( $text ); $text = esc_html( $text ); $text = trim( $text ); @@ -331,10 +331,7 @@ protected function sanitize_tested_version( $version ) { if ( $version ) { // Handle the edge-case of 'WordPress 5.0' and 'WP 5.0' for historical purposes. - $strip_phrases = [ - 'WordPress', - 'WP', - ]; + $strip_phrases = array( 'WordPress', 'WP' ); $version = trim( str_ireplace( $strip_phrases, '', $version ) ); // Strip off any -alpha, -RC, -beta suffixes, as these complicate comparisons and are rarely used. @@ -370,13 +367,7 @@ protected function sanitize_requires_version( $version ) { if ( $version ) { // Handle the edge-case of 'WordPress 5.0' and 'WP 5.0' for historical purposes. - $strip_phrases = [ - 'WordPress', - 'WP', - 'or higher', - 'and above', - '+', - ]; + $strip_phrases = array( 'WordPress', 'WP', 'or higher', 'and above', '+' ); $version = trim( str_ireplace( $strip_phrases, '', $version ) ); // Strip off any -alpha, -RC, -beta suffixes, as these complicate comparisons and are rarely used. From eb309df9e7774c4a0f114298ef8130019f205e4f Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Tue, 10 Aug 2021 13:12:39 +0200 Subject: [PATCH 04/13] Fix coding standard issue. --- checks/class-readme-parser.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/checks/class-readme-parser.php b/checks/class-readme-parser.php index 2f56bdb4..d5f1610a 100644 --- a/checks/class-readme-parser.php +++ b/checks/class-readme-parser.php @@ -191,7 +191,9 @@ protected function parse_readme_contents( $contents ) { // Handle readme's which do `=== Theme Name ===\nMy SuperAwesome Name\n...` if ( 'theme name' == strtolower( $this->name ) ) { - $this->name = $line = $this->get_first_nonwhitespace( $contents ); + + $line = $this->get_first_nonwhitespace( $contents ); + $this->name = $line; // Ensure that the line read wasn't an actual header or description. if ( strlen( $line ) > 50 || preg_match( '~^(' . implode( '|', array_keys( $this->valid_headers ) ) . ')\s*:~i', $line ) ) { From c660260fe5055fd653ca979094a102de6b577300 Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Tue, 10 Aug 2021 13:15:20 +0200 Subject: [PATCH 05/13] Update class-readme-check.php --- checks/class-readme-check.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/checks/class-readme-check.php b/checks/class-readme-check.php index 2c5013e0..db817152 100644 --- a/checks/class-readme-check.php +++ b/checks/class-readme-check.php @@ -2,6 +2,8 @@ /** * Validate readme files. * + * @link https://meta.svn.wordpress.org/sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/readme/ + * * @package Theme Check */ From c3e34ac870d77c93b25cc8f3f5407b2fbd6f58fc Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Tue, 10 Aug 2021 13:16:41 +0200 Subject: [PATCH 06/13] more CS issues.. --- checks/class-readme-parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/class-readme-parser.php b/checks/class-readme-parser.php index d5f1610a..3ccab1dc 100644 --- a/checks/class-readme-parser.php +++ b/checks/class-readme-parser.php @@ -192,7 +192,7 @@ protected function parse_readme_contents( $contents ) { // Handle readme's which do `=== Theme Name ===\nMy SuperAwesome Name\n...` if ( 'theme name' == strtolower( $this->name ) ) { - $line = $this->get_first_nonwhitespace( $contents ); + $line = $this->get_first_nonwhitespace( $contents ); $this->name = $line; // Ensure that the line read wasn't an actual header or description. From 8f3ed7525438cd10ffdb099ed64c97c2ff1921ce Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Thu, 12 Aug 2021 12:04:02 +0200 Subject: [PATCH 07/13] Update class-readme-check.php --- checks/class-readme-check.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/checks/class-readme-check.php b/checks/class-readme-check.php index db817152..8e1306e9 100644 --- a/checks/class-readme-check.php +++ b/checks/class-readme-check.php @@ -15,13 +15,6 @@ class Readme_Check implements themecheck { */ protected $error = array(); - /** - * Latest WordPress version - * - * @var string $latest_wordpress_version - */ - protected $latest_wordpress_version = '5.8'; - /** * Check that return true for good/okay/acceptable, false for bad/not-okay/unacceptable. * @@ -31,6 +24,13 @@ class Readme_Check implements themecheck { */ public function check( $php_files, $css_files, $other_files ) { + /** + * Latest WordPress version + * + * @var string $latest_wordpress_version + */ + $latest_wordpress_version = '5.8'; + checkcount(); // Get a list of file names and check for the readme. @@ -143,13 +143,13 @@ public function check( $php_files, $css_files, $other_files ) { ); } - if ( isset( $readme->warnings['contributor_ignored'] ) ) { + if ( 2 <= count( $readme->contributors ) ) { $this->error[] = sprintf( '%s: %s', __( 'README WARNING', 'theme-check' ), sprintf( /* translators: %s: theme header tag */ - __( 'One or more contributors listed in the readme were ignored. The %s field should only contain WordPress.org usernames. Remember that usernames are case-sensitive.', 'theme-check' ), + __( 'The %s field should only contain one WordPress.org username. Remember that usernames are case-sensitive.', 'theme-check' ), 'Contributors' ) ); @@ -159,7 +159,7 @@ public function check( $php_files, $css_files, $other_files ) { __( 'README WARNING', 'theme-check' ), sprintf( /* translators: %s: theme header tag */ - __( 'The %s field is missing from the readme.', 'theme-check' ), + __( 'The %s field is missing from the readme or is empty.', 'theme-check' ), 'Contributors' ) ); From d1d03187c3fcf963e6dfe6148ab6c9f8d0fb82af Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Fri, 13 Aug 2021 07:33:45 +0200 Subject: [PATCH 08/13] Update checks/class-readme-check.php Co-authored-by: Dion Hulse --- checks/class-readme-check.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/checks/class-readme-check.php b/checks/class-readme-check.php index 8e1306e9..966d306c 100644 --- a/checks/class-readme-check.php +++ b/checks/class-readme-check.php @@ -29,7 +29,13 @@ public function check( $php_files, $css_files, $other_files ) { * * @var string $latest_wordpress_version */ - $latest_wordpress_version = '5.8'; + if ( defined( 'WP_CORE_LATEST_RELEASE' ) ) { + // When running on WordPress.org, this constant defines the latest WordPress release. + $latest_wordpress_version = WP_CORE_LATEST_RELEASE; + } else { + // Assume that the local environment being tested in is up to date. + $latest_wordpress_version = $GLOBALS['wp_version']; + } checkcount(); From 8e696d2479a9e4ff7ddf01467bd5a9ba6847973c Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Thu, 19 Aug 2021 11:55:47 +0200 Subject: [PATCH 09/13] Check if the name in the readme matches the theme name Check if the name in the readme matches the theme name, if not, error. Update the $latest_wordpres_version variable. Remove empty lines to prevent errors with the check. --- checks/class-readme-check.php | 30 +++++++++++++++++++++++++++++- checks/class-readme-parser.php | 16 +++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/checks/class-readme-check.php b/checks/class-readme-check.php index 966d306c..74e52939 100644 --- a/checks/class-readme-check.php +++ b/checks/class-readme-check.php @@ -8,12 +8,26 @@ */ class Readme_Check implements themecheck { + /** * Error messages, warnings and info notices. * * @var array $error */ protected $error = array(); + + /** + * Theme information. Author URI, theme URI, Author name + * + * @var object $theme + */ + protected $theme; + + function set_context( $data ) { + if ( isset( $data['theme'] ) ) { + $this->theme = $data['theme']; + } + } /** * Check that return true for good/okay/acceptable, false for bad/not-okay/unacceptable. @@ -61,7 +75,7 @@ public function check( $php_files, $css_files, $other_files ) { // Parse the content of the readme. $readme = new Readme_Parser( $readme ); - // Error. + // Error if the theme name is missing in the readme. if ( empty( $readme->name ) ) { $this->error[] = sprintf( '%s: %s', @@ -74,6 +88,20 @@ public function check( $php_files, $css_files, $other_files ) { ) ); $ret = false; + } else if ( $readme->name != $this->theme ) { + $this->error[] = sprintf( + '%s: %s', + __( 'README ERROR', 'theme-check' ), + /* translators: 1: actual theme name, 2: theme name in readme, 3: 'Theme Name' section title, 4: 'Theme Name' */ + sprintf( + __( 'The theme name in the readme %1$s does not match the name of your theme %2$s. Theme name format looks like: %3$s. Please change %4$s to reflect the actual name of your theme.', 'theme-check' ), + '' . esc_html( $readme->name ) . '', + '' . esc_html( $this->theme ) . '', + '=== Theme Name ===', + 'Theme Name' + ) + ); + $ret = false; } // Warnings. diff --git a/checks/class-readme-parser.php b/checks/class-readme-parser.php index 3ccab1dc..1de654a9 100644 --- a/checks/class-readme-parser.php +++ b/checks/class-readme-parser.php @@ -167,6 +167,7 @@ protected function parse_readme_contents( $contents ) { } else { $contents = preg_split( '!\R!', $contents ); // regex failed due to invalid UTF8 in $contents, see #2298 } + // Remove empty lines. $contents = array_map( array( $this, 'strip_newlines' ), $contents ); // Strip UTF8 BOM if present. @@ -181,6 +182,8 @@ protected function parse_readme_contents( $contents ) { } } + $contents = array_filter( $contents, 'strlen' ); + $line = $this->get_first_nonwhitespace( $contents ); $this->name = $this->sanitize_text( trim( $line, "#= \t\0\x0B" ) ); @@ -329,7 +332,18 @@ protected function sanitize_requires_php( $version ) { */ protected function sanitize_tested_version( $version ) { $version = trim( $version ); - $latest_wordpress_version = '5.8'; + /** + * Latest WordPress version + * + * @var string $latest_wordpress_version + */ + if ( defined( 'WP_CORE_LATEST_RELEASE' ) ) { + // When running on WordPress.org, this constant defines the latest WordPress release. + $latest_wordpress_version = WP_CORE_LATEST_RELEASE; + } else { + // Assume that the local environment being tested in is up to date. + $latest_wordpress_version = $GLOBALS['wp_version']; + } if ( $version ) { // Handle the edge-case of 'WordPress 5.0' and 'WP 5.0' for historical purposes. From 5994c3c4be0d0c331c85829f864db6bf04fc2caf Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Thu, 19 Aug 2021 12:01:21 +0200 Subject: [PATCH 10/13] Solve CS problems --- checks/class-readme-check.php | 8 ++++---- checks/class-readme-parser.php | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/checks/class-readme-check.php b/checks/class-readme-check.php index 74e52939..ed76dd4e 100644 --- a/checks/class-readme-check.php +++ b/checks/class-readme-check.php @@ -8,14 +8,14 @@ */ class Readme_Check implements themecheck { - + /** * Error messages, warnings and info notices. * * @var array $error */ protected $error = array(); - + /** * Theme information. Author URI, theme URI, Author name * @@ -88,14 +88,14 @@ public function check( $php_files, $css_files, $other_files ) { ) ); $ret = false; - } else if ( $readme->name != $this->theme ) { + } elseif ( $readme->name != $this->theme ) { $this->error[] = sprintf( '%s: %s', __( 'README ERROR', 'theme-check' ), /* translators: 1: actual theme name, 2: theme name in readme, 3: 'Theme Name' section title, 4: 'Theme Name' */ sprintf( __( 'The theme name in the readme %1$s does not match the name of your theme %2$s. Theme name format looks like: %3$s. Please change %4$s to reflect the actual name of your theme.', 'theme-check' ), - '' . esc_html( $readme->name ) . '', + '' . esc_html( $readme->name ) . '', '' . esc_html( $this->theme ) . '', '=== Theme Name ===', 'Theme Name' diff --git a/checks/class-readme-parser.php b/checks/class-readme-parser.php index 1de654a9..6b4521bb 100644 --- a/checks/class-readme-parser.php +++ b/checks/class-readme-parser.php @@ -182,7 +182,7 @@ protected function parse_readme_contents( $contents ) { } } - $contents = array_filter( $contents, 'strlen' ); + $contents = array_filter( $contents, 'strlen' ); $line = $this->get_first_nonwhitespace( $contents ); $this->name = $this->sanitize_text( trim( $line, "#= \t\0\x0B" ) ); @@ -331,7 +331,8 @@ protected function sanitize_requires_php( $version ) { * @return string The sanitized $version */ protected function sanitize_tested_version( $version ) { - $version = trim( $version ); + $version = trim( $version ); + /** * Latest WordPress version * From 3995c79cbed71273b4e4ba35ec4f70b27ba275e8 Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Thu, 19 Aug 2021 12:05:18 +0200 Subject: [PATCH 11/13] Fix CS problems --- checks/class-readme-parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/class-readme-parser.php b/checks/class-readme-parser.php index 6b4521bb..e10b3c23 100644 --- a/checks/class-readme-parser.php +++ b/checks/class-readme-parser.php @@ -182,7 +182,7 @@ protected function parse_readme_contents( $contents ) { } } - $contents = array_filter( $contents, 'strlen' ); + $contents = array_filter( $contents, 'strlen' ); $line = $this->get_first_nonwhitespace( $contents ); $this->name = $this->sanitize_text( trim( $line, "#= \t\0\x0B" ) ); From 35bc24902233fd34c3df37cf5febac44fb8a68ca Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Tue, 24 Aug 2021 15:44:07 +0200 Subject: [PATCH 12/13] Fix for 392 Try to solve timeout issues. Check for uppercase file names as well as lowercase with stripos. --- checks/class-readme-check.php | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/checks/class-readme-check.php b/checks/class-readme-check.php index ed76dd4e..3b94a376 100644 --- a/checks/class-readme-check.php +++ b/checks/class-readme-check.php @@ -23,10 +23,20 @@ class Readme_Check implements themecheck { */ protected $theme; + /** + * Theme slug. + * + * @var object $slug + */ + protected $slug; + function set_context( $data ) { if ( isset( $data['theme'] ) ) { $this->theme = $data['theme']; } + if ( isset( $data['slug'] ) ) { + $this->slug = $data['slug']; + } } /** @@ -54,17 +64,17 @@ public function check( $php_files, $css_files, $other_files ) { checkcount(); // Get a list of file names and check for the readme. - $other_filenames = array(); + $readme = ''; + + // Get the contents of themeslug/filename: foreach ( $other_files as $path => $contents ) { - $other_filenames[] = tc_filename( $path ); - if ( tc_filename( $path ) == 'readme.txt' || tc_filename( $path ) == 'readme.md' ) { - $readme = $contents; - break; + if ( stripos( $path, $this->slug . '/readme.txt' ) || stripos( $path, $this->slug . '/readme.md' ) !== false ) { + $readme .= $contents; } } // Publish an error if there is no readme file. - if ( ! in_array( 'readme.txt', $other_filenames, true ) && ! in_array( 'readme.md', $other_filenames, true ) ) { + if ( empty( $readme) ) { $this->error[] = sprintf( '%s: %s', __( 'REQUIRED', 'theme-check' ), From 97a863b87ac705caee082a7ddc163dc82eb8de1d Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Tue, 24 Aug 2021 15:52:44 +0200 Subject: [PATCH 13/13] fix coding standard issues --- checks/class-readme-check.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/checks/class-readme-check.php b/checks/class-readme-check.php index 3b94a376..99bf0a43 100644 --- a/checks/class-readme-check.php +++ b/checks/class-readme-check.php @@ -64,7 +64,7 @@ public function check( $php_files, $css_files, $other_files ) { checkcount(); // Get a list of file names and check for the readme. - $readme = ''; + $readme = ''; // Get the contents of themeslug/filename: foreach ( $other_files as $path => $contents ) { @@ -74,7 +74,7 @@ public function check( $php_files, $css_files, $other_files ) { } // Publish an error if there is no readme file. - if ( empty( $readme) ) { + if ( empty( $readme ) ) { $this->error[] = sprintf( '%s: %s', __( 'REQUIRED', 'theme-check' ),