Changeset 13443 for sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/readme/class-validator.php
- Timestamp:
- 04/03/2024 07:01:21 AM (6 months ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/readme/class-validator.php
r13267 r13443 63 63 */ 64 64 public function validate_content( $readme ) { 65 $output = $this->validate( $readme ); 66 67 // Translate error codes to human-readable messages. 68 foreach ( $output as $type => $items ) { 69 foreach ( $items as $error_code => $error_data ) { 70 $output[ $type ][ $error_code ] = $this->translate_code_to_message( $error_code, $error_data ); 71 } 72 } 73 74 return $output; 75 } 76 77 /** 78 * Validates a readme by string, and returns a structured array of errors, warnings, and notes. 79 * 80 * These elements can be swapped for a textual translated string at display time via translate_code_to_message(). 81 * 82 * @param string $readme The text of the readme. 83 * @return array Array of the readme validation result codes. 84 */ 85 public function validate( $readme ) { 86 $errors = $warnings = $notes = array(); 65 87 66 88 // Security note: Keep the data: protocol here, Parser accepts a string HOWEVER … … 69 91 $readme = new Parser( 'data:text/plain,' . urlencode( $readme ) ); 70 92 71 $errors = $warnings = $notes = array();72 73 93 // Fatal errors. 74 if ( empty( $readme->name ) || isset( $readme->warnings['invalid_plugin_name_header'] ) ) { 75 $errors[] = sprintf( 76 /* translators: 1: 'Plugin Name' section title, 2: 'Plugin Name' */ 77 __( 'We cannot find a plugin name in your readme. Plugin names look like: %1$s. Please change %2$s to reflect the actual name of your plugin.', 'wporg-plugins' ), 78 '<code>=== Plugin Name ===</code>', 79 '<code>Plugin Name</code>' 80 ); 94 if ( isset( $readme->warnings['invalid_plugin_name_header'] ) ) { 95 $errors['invalid_plugin_name_header'] = $readme->warnings['invalid_plugin_name_header']; 96 } elseif ( empty( $readme->name ) ) { 97 $errors['invalid_plugin_name_header'] = true; 81 98 } 82 99 83 100 // Warnings & Notes. 84 101 if ( isset( $readme->warnings['requires_header_ignored'] ) ) { 85 $latest_wordpress_version = defined( 'WP_CORE_STABLE_BRANCH' ) ? WP_CORE_STABLE_BRANCH : '5.0'; 86 87 $warnings[] = sprintf( 88 /* translators: 1: plugin header tag; 2: Example version 5.0. 3: Example version 4.9. */ 89 __( 'The %1$s field was ignored. This field should only contain a valid WordPress version such as %2$s or %3$s.', 'wporg-plugins' ), 90 '<code>Requires at least</code>', 91 '<code>' . number_format( $latest_wordpress_version, 1 ) . '</code>', 92 '<code>' . number_format( $latest_wordpress_version - 0.1, 1 ) . '</code>' 102 $warnings['requires_header_ignored'] = $readme->warnings['requires_header_ignored']; 103 } 104 105 if ( isset( $readme->warnings['tested_header_ignored'] ) ) { 106 $warnings['tested_header_ignored'] = $readme->warnings['tested_header_ignored']; 107 } elseif ( empty( $readme->tested ) ) { 108 $warnings['tested_header_missing'] = true; 109 } 110 111 if ( isset( $readme->warnings['requires_php_header_ignored'] ) ) { 112 $warnings['requires_php_header_ignored'] = $readme->warnings['requires_php_header_ignored']; 113 } 114 115 if ( empty( $readme->stable_tag ) || str_contains( $readme->stable_tag, 'trunk' ) ) { 116 $warnings['stable_tag_invalid'] = true; 117 } 118 119 if ( isset( $readme->warnings['contributor_ignored'] ) ) { 120 $warnings['contributor_ignored'] = $readme->warnings['contributor_ignored']; 121 } elseif ( ! count( $readme->contributors ) ) { 122 $notes['contributors_missing'] = true; 123 } 124 125 if ( empty( $readme->license ) ) { 126 $warnings['license_missing'] = true; 127 } 128 129 if ( isset( $readme->warnings['too_many_tags'] ) ) { 130 $warnings['too_many_tags'] = $readme->warnings['too_many_tags']; 131 } 132 133 if ( isset( $readme->warnings['ignored_tags'] ) ) { 134 $warnings['ignored_tags'] = $readme->warnings['ignored_tags']; 135 } 136 137 // Check if the tags are low-quality (ie. little used) 138 if ( $readme->tags && taxonomy_exists( 'plugin_tags' ) ) { 139 $tags = get_terms( array( 140 'taxonomy' => 'plugin_tags', 141 'name' => $readme->tags, 142 ) ); 143 144 $low_usage_tags = array_filter( 145 $tags, 146 function( $term ) { 147 return $term->count < 5; 148 } 93 149 ); 94 } 95 96 if ( isset( $readme->warnings['tested_header_ignored'] ) ) { 97 $latest_wordpress_version = defined( 'WP_CORE_STABLE_BRANCH' ) ? WP_CORE_STABLE_BRANCH : '5.0'; 98 99 $warnings[] = sprintf( 100 /* translators: 1: plugin header tag; 2: Example version 5.0. 3: Example version 5.1. */ 101 __( 'The %1$s field was ignored. This field should only contain a valid WordPress version such as %2$s or %3$s.', 'wporg-plugins' ), 102 '<code>Tested up to</code>', 103 '<code>' . number_format( $latest_wordpress_version, 1 ) . '</code>', 104 '<code>' . number_format( $latest_wordpress_version + 0.1, 1 ) . '</code>' 105 ); 106 } elseif ( empty( $readme->tested ) ) { 107 $warnings[] = sprintf( 108 /* translators: %s: plugin header tag */ 109 __( 'The %s field is missing.', 'wporg-plugins' ), 110 '<code>Tested up to</code>' 111 ); 112 } 113 114 if ( isset( $readme->warnings['requires_php_header_ignored'] ) ) { 115 $warnings[] = sprintf( 116 /* translators: 1: plugin header tag; 2: Example version 5.2.4. 3: Example version 7.0. */ 117 __( 'The %1$s field was ignored. This field should only contain a PHP version such as %2$s or %3$s.', 'wporg-plugins' ), 118 '<code>Requires PHP</code>', 119 '<code>5.2.4</code>', 120 '<code>7.0</code>' 121 ); 122 } 123 124 if ( empty( $readme->stable_tag ) || str_contains( $readme->stable_tag, 'trunk' ) ) { 125 $warnings[] = sprintf( 126 /* translators: 1: 'Stable tag', 2: /trunk/ SVN directory */ 127 __( 'The %1$s field is missing or invalid. Note: We ask you no longer attempt to use %2$s as stable, so that all plugins can be rolled back.', 'wporg-plugins' ), 128 '<code>Stable tag</code>', 129 '<code>/trunk/</code>' 130 ); 131 } 132 133 if ( isset( $readme->warnings['contributor_ignored'] ) ) { 134 $warnings[] = sprintf( 135 /* translators: %s: plugin header tag */ 136 __( 'One or more contributors listed were ignored. The %s field should only contain WordPress.org usernames. Remember that usernames are case-sensitive.', 'wporg-plugins' ), 137 '<code>Contributors</code>' 138 ); 139 } elseif ( ! count( $readme->contributors ) ) { 140 $notes[] = sprintf( 141 /* translators: %s: plugin header tag */ 142 __( 'The %s field is missing.', 'wporg-plugins' ), 143 '<code>Contributors</code>' 144 ); 145 } 146 147 if ( isset( $readme->warnings['too_many_tags'] ) ) { 148 $warnings[] = __( 'One or more tags were ignored. Please limit your plugin to 5 tags.', 'wporg-plugins' ); 149 } 150 151 if ( isset( $readme->warnings['ignored_tags'] ) ) { 152 $warnings[] = sprintf( 153 /* translators: %s: list of tags not supported */ 154 __( 'One or more tags were ignored. The following tags are not permitted: %s', 'wporg-plugins' ), 155 '<code>' . implode( '</code>, <code>', $readme->ignore_tags ) . '</code>' 156 ); 157 } 158 159 if ( isset( $readme->warnings['low_usage_tags'] ) ) { 160 $notes[] = sprintf( 161 /* translators: %s: list of tags with low usage. */ 162 __( 'The following tags are not widely used: %s', 'wporg-plugins' ), 163 '<code>' . implode( '</code>, <code>', array_map( 'esc_html', $readme->warnings['low_usage_tags'] ) ) . '</code>' 164 ); 150 151 if ( $low_usage_tags ) { 152 $notes['low_usage_tags'] = wp_list_pluck( $low_usage_tags, 'name' ); 153 } 165 154 } 166 155 167 156 if ( empty( $readme->requires ) ) { 168 $notes[] = sprintf( 169 /* translators: %s: plugin header tag */ 170 __( 'The %s field is missing. It should be defined here, or in your main plugin file.', 'wporg-plugins' ), 171 '<code>Requires at least</code>' 172 ); 157 $notes['requires_header_missing'] = true; 173 158 } 174 159 175 160 if ( empty( $readme->requires_php ) ) { 176 $notes[] = sprintf( 177 /* translators: %s: plugin header tag */ 178 __( 'The %s field is missing. It should be defined here, or in your main plugin file.', 'wporg-plugins' ), 179 '<code>Requires PHP</code>' 180 ); 161 $notes['requires_php_header_missing'] = true; 181 162 } 182 163 183 164 if ( isset( $readme->warnings['no_short_description_present'] ) ) { 184 $notes[] = sprintf( 185 /* translators: %s: section title */ 186 __( 'The %s section is missing. An excerpt was generated from your main plugin description.', 'wporg-plugins' ), 187 '<code>Short Description</code>' 188 ); 189 } elseif( isset( $readme->warnings['trimmed_short_description'] ) ) { 190 $warnings[] = sprintf( 191 /* translators: %s: section title */ 192 __( 'The %s section is too long and was truncated. A maximum of 150 characters is supported.', 'wporg-plugins' ), 193 '<code>Short Description</code>', 194 number_format_i18n( $readme->maximum_field_lengths['short_description'] ) 195 ); 165 $notes['no_short_description_present'] = $readme->warnings['no_short_description_present']; 166 167 } elseif ( isset( $readme->warnings['trimmed_short_description'] ) ) { 168 $warnings['trimmed_short_description'] = $readme->warnings['trimmed_short_description']; 196 169 } 197 170 … … 200 173 }, ARRAY_FILTER_USE_KEY ); 201 174 foreach ( $trimmed_sections as $section_name => $dummy ) { 202 $section_name = str_replace( 'trimmed_section_', '', $section_name ); 203 204 $max_length_field = "section-{$section_name}"; 205 if ( ! isset( $readme->maximum_field_lengths[ $max_length_field ] ) ) { 206 $max_length_field = 'section'; 207 } 208 209 $warnings[] = sprintf( 210 /* translators: %s: section title */ 211 __( 'The %s section is too long and was truncated. A maximum of %s words is supported.', 'wporg-plugins' ), 212 '<code>' . esc_html( ucwords( str_replace( '_', ' ', $section_name ) ) ) . '</code>', 213 number_format_i18n( $readme->maximum_field_lengths[ $max_length_field ] ) 214 ); 175 $warnings[ $section_name ] = true; 215 176 } 216 177 217 178 if ( empty( $readme->sections['faq'] ) ) { 218 $notes[] = sprintf( 219 /* translators: %s: section title */ 220 __( 'No %s section was found', 'wporg-plugins' ), 221 '<code>== Frequently Asked Questions ==</code>' 222 ); 179 $notes['faq_missing'] = true; 223 180 } 224 181 225 182 if ( empty( $readme->sections['changelog'] ) ) { 226 $notes[] = sprintf( 227 /* translators: %s: section title */ 228 __( 'No %s section was found', 'wporg-plugins' ), 229 '<code>== Changelog ==</code>' 230 ); 183 $notes['changelog_missing'] = true; 231 184 } 232 185 233 186 if ( empty( $readme->upgrade_notice ) ) { 234 $notes[] = sprintf( 235 /* translators: %s: section title */ 236 __( 'No %s section was found', 'wporg-plugins' ), 237 '<code>== Upgrade Notice ==</code>' 238 ); 187 $notes['upgrade_notice_missing'] = true; 239 188 } 240 189 241 190 if ( empty( $readme->screenshots ) ) { 242 $notes[] = sprintf( 243 /* translators: %s: section title */ 244 __( 'No %s section was found', 'wporg-plugins' ), 245 '<code>== Screenshots ==</code>' 246 ); 191 $notes['screenshots_missing'] = true; 247 192 } 248 193 249 194 if ( empty( $readme->donate_link ) ) { 250 $notes[ ] = __( 'No donate link was found', 'wporg-plugins' );195 $notes['donate_link_missing'] = true; 251 196 } 252 197 253 198 return compact( 'errors', 'warnings', 'notes' ); 254 255 199 } 256 200 201 /** 202 * Translate an error code to a human-readable message. 203 * 204 * @param string $error_code The error code to translate. 205 * @param mixed $data Optional data to provide context in the message. 206 * @return string|false The translated message, or false if the error code is not recognized. 207 */ 208 public function translate_code_to_message( $error_code, $data = false ) { 209 if ( $data && is_bool( $data ) ) { 210 $data = false; 211 } 212 213 switch( $error_code ) { 214 case 'invalid_plugin_name_header': 215 return sprintf( 216 /* translators: 1: 'Plugin Name' section title, 2: 'Plugin Name' */ 217 __( 'We cannot find a plugin name in your readme. Plugin names look like: %1$s. Please change %2$s to reflect the actual name of your plugin.', 'wporg-plugins' ), 218 '<code>=== Plugin Name ===</code>', 219 '<code>Plugin Name</code>' 220 ); 221 case 'requires_header_ignored': 222 $latest_wordpress_version = defined( 'WP_CORE_STABLE_BRANCH' ) ? WP_CORE_STABLE_BRANCH : '6.5'; 223 224 return sprintf( 225 /* translators: 1: plugin header tag; 2: Example version 5.0. 3: Example version 4.9. */ 226 __( 'The %1$s field was ignored. This field should only contain a valid WordPress version such as %2$s or %3$s.', 'wporg-plugins' ), 227 '<code>Requires at least</code>', 228 '<code>' . number_format( $latest_wordpress_version, 1 ) . '</code>', 229 '<code>' . number_format( $latest_wordpress_version - 0.1, 1 ) . '</code>' 230 ); 231 case 'tested_header_ignored': 232 $latest_wordpress_version = defined( 'WP_CORE_STABLE_BRANCH' ) ? WP_CORE_STABLE_BRANCH : '6.5'; 233 234 return sprintf( 235 /* translators: 1: plugin header tag; 2: Example version 5.0. 3: Example version 5.1. */ 236 __( 'The %1$s field was ignored. This field should only contain a valid WordPress version such as %2$s or %3$s.', 'wporg-plugins' ), 237 '<code>Tested up to</code>', 238 '<code>' . number_format( $latest_wordpress_version, 1 ) . '</code>', 239 '<code>' . number_format( $latest_wordpress_version + 0.1, 1 ) . '</code>' 240 ); 241 case 'tested_header_missing': 242 return sprintf( 243 /* translators: %s: plugin header tag */ 244 __( 'The %s field is missing.', 'wporg-plugins' ), 245 '<code>Tested up to</code>' 246 ); 247 case 'requires_header_missing': 248 return sprintf( 249 /* translators: %s: plugin header tag */ 250 __( 'The %s field is missing. It should be defined here, or in your main plugin file.', 'wporg-plugins' ), 251 '<code>Requires at least</code>' 252 ); 253 case 'requires_php_header_ignored': 254 global $required_php_version; // WP wp-includes/version.php. 255 return sprintf( 256 /* translators: 1: plugin header tag; 2: Example version 7.0. 3: Example version 8.2. */ 257 __( 'The %1$s field was ignored. This field should only contain a PHP version such as %2$s or %3$s.', 'wporg-plugins' ), 258 '<code>Requires PHP</code>', 259 '<code>' . esc_html( $required_php_version ?? '7.0' ) . '</code>', 260 '<code>8.2</code>' 261 ); 262 case 'requires_php_header_missing': 263 return sprintf( 264 /* translators: %s: plugin header tag */ 265 __( 'The %s field is missing. It should be defined here, or in your main plugin file.', 'wporg-plugins' ), 266 '<code>Requires PHP</code>' 267 ); 268 case 'stable_tag_invalid': 269 return sprintf( 270 /* translators: 1: 'Stable tag', 2: /trunk/ SVN directory */ 271 __( 'The %1$s field is missing or invalid. Note: We ask you no longer attempt to use %2$s as stable, so that all plugins can be rolled back.', 'wporg-plugins' ), 272 '<code>Stable tag</code>', 273 '<code>/trunk/</code>' 274 ); 275 case 'contributor_ignored': 276 if ( ! $data ) { 277 return sprintf( 278 /* translators: %s: plugin header tag */ 279 __( 'One or more contributors listed were ignored. The %s field should only contain WordPress.org usernames.', 'wporg-plugins' ), 280 '<code>Contributors</code>' 281 ); 282 } else { 283 return sprintf( 284 /* translators: 1: List of authors from the readme 2: plugin header tag */ 285 __( 'The following contributors listed were ignored, as the WordPress.org user could not be found. %1$s. The %2$s field should only contain WordPress.org usernames.', 'wporg-plugins' ), 286 '<code>' . implode( '</code>, <code>', array_map( 'esc_html', $data ) ) . '</code>', 287 '<code>Contributors</code>' 288 ); 289 } 290 case 'contributors_missing': 291 return sprintf( 292 /* translators: %s: plugin header tag */ 293 __( 'The %s field is missing.', 'wporg-plugins' ), 294 '<code>Contributors</code>' 295 ); 296 case 'too_many_tags': 297 if ( $data ) { 298 return sprintf( 299 /* translators: %s: list of tags not supported */ 300 __( 'One or more tags were ignored: %s. Please limit your plugin to 5 tags.', 'wporg-plugins' ), 301 '<code>' . implode( '</code>, <code>', array_map( 'esc_html', $data ) ) . '</code>' 302 ); 303 } else { 304 return __( 'One or more tags were ignored. Please limit your plugin to 5 tags.', 'wporg-plugins' ); 305 } 306 case 'ignored_tags': 307 return sprintf( 308 /* translators: %s: list of tags not supported */ 309 __( 'One or more tags were ignored. The following tags are not permitted: %s', 'wporg-plugins' ), 310 '<code>' . implode( '</code>, <code>', $readme->ignore_tags ) . '</code>' 311 ); 312 case 'low_usage_tags': 313 return sprintf( 314 /* translators: %s: list of tags with low usage. */ 315 __( 'The following tags are not widely used: %s', 'wporg-plugins' ), 316 '<code>' . implode( '</code>, <code>', array_map( 'esc_html', $data ) ) . '</code>' 317 ); 318 case 'no_short_description_present': 319 return sprintf( 320 /* translators: %s: section title */ 321 __( 'The %s section is missing. An excerpt was generated from your main plugin description.', 'wporg-plugins' ), 322 '<code>Short Description</code>' 323 ); 324 case 'trimmed_short_description': 325 return sprintf( 326 /* translators: %s: section title */ 327 __( 'The %s section is too long and was truncated. A maximum of %s characters is supported.', 'wporg-plugins' ), 328 '<code>Short Description</code>', 329 number_format_i18n( (new Parser)->maximum_field_lengths['short_description'] ) 330 ); 331 case 'trimmed_section_description': 332 case 'trimmed_section_installation': 333 case 'trimmed_section_faq': 334 case 'trimmed_section_screenshots': 335 case 'trimmed_section_changelog': 336 case 'trimmed_section_upgrade_notice': 337 case 'trimmed_section_other_notes': 338 $readme = new Parser; 339 $section_name = str_replace( 'trimmed_section_', '', $error_code ); 340 341 $max_length_field = "section-{$section_name}"; 342 if ( ! isset( $readme->maximum_field_lengths[ $max_length_field ] ) ) { 343 $max_length_field = 'section'; 344 } 345 346 return sprintf( 347 /* translators: %s: section title */ 348 __( 'The %s section is too long and was truncated. A maximum of %s words is supported.', 'wporg-plugins' ), 349 '<code>' . esc_html( ucwords( str_replace( '_', ' ', $section_name ) ) ) . '</code>', 350 number_format_i18n( $readme->maximum_field_lengths[ $max_length_field ] ) 351 ); 352 case 'faq_missing': 353 return sprintf( 354 /* translators: %s: section title */ 355 __( 'No %s section was found', 'wporg-plugins' ), 356 '<code>== Frequently Asked Questions ==</code>' 357 ); 358 case 'changelog_missing': 359 return sprintf( 360 /* translators: %s: section title */ 361 __( 'No %s section was found', 'wporg-plugins' ), 362 '<code>== Changelog ==</code>' 363 ); 364 case 'upgrade_notice_missing': 365 return sprintf( 366 /* translators: %s: section title */ 367 __( 'No %s section was found', 'wporg-plugins' ), 368 '<code>== Upgrade Notice ==</code>' 369 ); 370 case 'screenshots_missing': 371 return sprintf( 372 /* translators: %s: section title */ 373 __( 'No %s section was found', 'wporg-plugins' ), 374 '<code>== Screenshots ==</code>' 375 ); 376 case 'donate_link_missing': 377 return __( 'No donate link was found', 'wporg-plugins' ); 378 case 'license_missing': 379 return sprintf( 380 /* translators: 1: 'License' */ 381 __( 'The %1$s field is missing or invalid. A GPLv2 or later compatible license should be specified.', 'wporg-plugins' ), 382 '<code>License</code>' 383 ); 384 385 /* The are not generated by the Readme Parser, but rather the import parser. */ 386 case 'invalid_update_uri': 387 return sprintf( 388 /* translators: %s 'Update URI' */ 389 __( 'The %s specified is invalid. This field should not be used with WordPress.org hosted plugins.', 'wporg-plugins' ), 390 '<code>Update URI</code>' 391 ); 392 case 'unmet_dependencies': 393 return sprintf( 394 /* translators: %s: list of plugin dependencies */ 395 __( 'Invalid plugin dependencies specified. The following dependencies could not be resolved: %s', 'wporg-plugins' ), 396 '<code>' . implode( '</code>, <code>', array_map( 'esc_html', $data ) ) . '</code>' 397 ); 398 } 399 400 return false; 401 } 402 257 403 }
Note: See TracChangeset
for help on using the changeset viewer.