Ticket #17902: 17902.patch
File 17902.patch, 30.0 KB (added by , 13 years ago) |
---|
-
wp-admin/includes/class-wp-plugin-readme-parser.php
1 <?php 2 3 /** 4 * Parse a plugin's readme.txt file 5 * Based on http://code.svn.wordpress.org/plugin-readme-parser/parse-readme.php 6 * @link http://wordpress.org/extend/plugins/about/readme.txt 7 * @link http://wordpress.org/extend/plugins/about/validator/ 8 * @pacakge WordPress 9 * @version 1.0 10 */ 11 class wp_plugin_readme_parser { 12 13 /** 14 * Readme.txt file contents 15 * This string will change with each pass in the process 16 * @var string 17 */ 18 protected $_readme_contents = ''; 19 20 /** 21 * Special section names 22 * @var array 23 */ 24 protected $_special_sections = array( 25 'description', 26 'installation', 27 'frequently_asked_questions', 28 'screenshots', 29 'changelog', 30 'change_log', 31 'upgrade_notice' 32 ); 33 34 /** 35 * Minimum version of WordPress 36 * @var string 37 */ 38 protected $_requires_at_least = ''; 39 40 /** 41 * Maximum compatible version of WordPress 42 * @var string 43 */ 44 protected $_tested_up_to = ''; 45 46 /** 47 * Stable tag for the plugin 48 * @var string 49 */ 50 protected $_stable_tag = ''; 51 52 /** 53 * Keywords for the plugin directory search 54 * @var array 55 */ 56 protected $_tags = array(); 57 58 /** 59 * Authors who wrote the plugin 60 * @var array 61 */ 62 protected $_contributors = array(); 63 64 /** 65 * Where to donate 66 * @var string 67 */ 68 protected $_donate_link = ''; 69 70 /** 71 * License type 72 * @var string 73 */ 74 protected $_license = ''; 75 76 /** 77 * Recognized keys for records 78 * @var array 79 */ 80 protected $_keys = array( 81 'requires_at_least' => '/Requires at least:[ \t]*(.+)/i', 82 'tested_up_to' => '/Tested up to:[ \t]*(.+)/i', 83 'stable_tag' => '/Stable tag:[ \t]*(.+)/i', 84 'tags' => '/Tags:[ \t]*(.+)/i', 85 'contributors' => '/Contributors:[ \t]*(.+)/i', 86 'donate_link' => '/Donate link:[ \t]*(.+)/i', 87 'license' => '/License:[ \t]*(.+)/i' 88 ); 89 90 /** 91 * Is the description a copy of the short description? 92 * @var bool 93 */ 94 protected $_is_excerpt = false; 95 96 /** 97 * Was the short description truncated to 150 characters? 98 * @var bool 99 */ 100 protected $_is_truncated = false; 101 102 /** 103 * Short description of the plugin 104 * @var string 105 */ 106 protected $_short_description = ''; 107 108 /** 109 * List of the plugin's screenshots 110 * @var array 111 */ 112 protected $_screenshots = array(); 113 114 /** 115 * Content outside of the standard sections 116 * @var string 117 */ 118 protected $_remaining_content = ''; 119 120 /** 121 * Upgrade notices for users 122 * Key = version, value = message. Example: 123 * 1.0 => Please upgrade 124 * 1.1 => Dire security bug 125 * 1.2 => Minor UI issue 126 * @var array 127 */ 128 protected $_upgrade_notice = array(); 129 130 /** 131 * Allowed tags in sections 132 * @var array 133 */ 134 private $_allowed_tags = array( 135 'a' => array( 136 'href' => array(), 137 'title' => array(), 138 'rel' => array() 139 ), 140 'blockquote' => array( 'cite' => array() ), 141 'br' => array(), 142 'cite' => array(), 143 'p' => array(), 144 'code' => array(), 145 'pre' => array(), 146 'em' => array(), 147 'strong' => array(), 148 'ul' => array(), 149 'ol' => array(), 150 'li' => array(), 151 'h3' => array(), 152 'h4' => array() 153 ); 154 155 /** 156 * Parse a plugin's readme.txt file. Expects a path to the file. 157 * @param string $file 158 * @return string 159 */ 160 public function parse_readme_file ( $file ) { 161 if ( !file_exists( $file ) ) { 162 throw new Exception('File not found'); 163 } 164 $this->_readme_contents = file_get_contents( $file ); 165 return $this->_parse_readme(); 166 } 167 168 /** 169 * Parse a plugin's readme.txt. Expect's the file contents as string. 170 * @param type $file_contents 171 * @return type 172 */ 173 public function parse_readme_contents( $file_contents ) { 174 $this->_readme_contents = $file_contents; 175 return $this->_parse_readme(); 176 } 177 178 /** 179 * Extract sections 180 * @return array 181 */ 182 private function _exract_sections( ) { 183 $ret = array(); 184 185 $sections = preg_split( '/^[\s]*==[\s]*(.+?)[\s]*==/m', $this->_readme_contents, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY ); 186 187 // Check if the first element is a short description 188 $this->_short_description = ''; 189 $this->_is_truncated = false; 190 $this->_is_excerpt = true; 191 if ( '==' != substr($sections[0][0], 0, 2) ) { 192 $short_description = array_shift( $sections ); 193 194 // If the user doesn't include a full description, this will be used, not truncated, and converted to markdown. If they 195 // do include a full description, this field will be overwritten 196 $ret['description'] = $this->_markdown( $short_description ); 197 198 $short_description = $this->_sanitize_text( $short_description ); 199 if ( strlen( $short_description ) > 150 ) 200 $this->_is_truncated = true; 201 $this->_short_description = substr( $short_description, 0, 150 ); 202 } 203 204 // Sanitize titles / sections 205 for ( $i = 0 ; $i < count( $sections ) ; $i += 2 ) { 206 $title = $this->_sanitize_text( $sections[$i] ); 207 $contents = preg_replace( '/^[\s]*=[\s]+(.+?)[\s]+=/m', '<h4>$1</h4>', $sections[$i+1] ); 208 $contents = $this->_markdown( $contents ); 209 $ret[$title] = $contents; 210 if ( $this->_is_excerpt && 'description' == strtolower( strip_tags( $title ) ) ) 211 $this->_is_excerpt = false; 212 } 213 214 // Remove the sections from the readme file 215 $this->_readme_contents = trim( str_replace( $sections, '', $this->_readme_contents ) ); 216 217 // Done 218 return $this->_process_sections( $ret ); 219 } 220 221 /** 222 * Post-process sections 223 * Any special business logic (e.g. change_log -> changelog) is done here 224 * @param array $sections 225 * @return array 226 */ 227 private function _process_sections( $sections ) { 228 229 // Rename sections to lower-case-underscore notation relegate non-special 230 // content to the "remaining content" section 231 $_sections = array(); 232 $this->_remaining_content = ''; 233 foreach ( (array) $sections as $k => $v ) { 234 $name = strtolower( preg_replace( '/[^a-zA-Z_]/', '_', $k ) ); 235 $_sections[$name] = $v; 236 237 // Is this "remaining content" ? 238 if ( !in_array( $name, $this->_special_sections ) ) { 239 $title_id = esc_attr( $k ); 240 $title_id = str_replace( ' ', '-', $title_id ); 241 $this->_remaining_content .= sprintf("\n<h3 id=\"%s\">%s</h3>\n%s", $name, $k, $v); 242 } 243 } 244 $sections = $_sections; 245 246 // Upgrade notice section 247 $this->_upgrade_notice = array(); 248 if ( array_key_exists( 'upgrade_notice', $sections ) ) { 249 $upgrade_notice = array(); 250 $notices = preg_split( '/<h4>(.*?)<\/h4>/', $sections['upgrade_notice'], -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); 251 if ( empty( $notices ) && count( $notices ) > 1 ) { 252 for ( $i = 0 ; $i < count( $notices ) ; $i += 2 ) 253 $upgrade_notice[$notices[$i]] = substr( $this->_sanitize_text( @$notices[$i+1] ), 0, 300 ); 254 } elseif ( is_array( $notices ) && 1 == count( $notices ) ) { 255 $upgrade_notice[ $this->_stable_tag ] = $notices[0]; 256 } 257 $this->_upgrade_notice = $upgrade_notice; 258 unset( $sections['upgrade_notice'] ); 259 } 260 261 // Screenshots 262 $this->_screenshots = array(); 263 if ( array_key_exists( 'screenshots', $sections ) ) { 264 preg_match_all( '/<li>(.*?)<\/li>/s', $sections['screenshots'], $screenshots, PREG_SET_ORDER); 265 if ( $screenshots ) { 266 foreach ( (array) $screenshots as $screenshot ) 267 $this->_screenshots[] = $screenshot[1]; 268 } 269 } 270 271 // Fix change_log -> changelog 272 if ( array_key_exists( 'change_log', $sections ) ) 273 $sections['changelog'] = $sections['change_log']; 274 275 return $sections; 276 } 277 278 /** 279 * Post-process records 280 * Any special business logic (e.g. splitting tags, contributors) is done here 281 * @param array $records 282 * @return array 283 */ 284 private function _process_records( $records ) { 285 286 // Escape donate link URL 287 if ( !empty( $records['donate_link'] ) ) 288 $records['donate_link'] = esc_url( $records['donate_link'] ); 289 290 // Split up tags 291 $tags = preg_split('/,[\s]*?/', trim( $records['tags'] ) ); 292 foreach ( (array) $tags as $k => $v ) { 293 $v = $this->_sanitize_text( $v ); 294 if ( !empty( $v ) ) 295 $tags[$k] = $this->_sanitize_text( $v ); 296 else 297 unset( $tags[$k] ); 298 } 299 $records['tags'] = $tags; 300 301 // Split up contributors 302 $contributors = preg_split( '/,[\s]*/', trim( $records['contributors'] ) ); 303 foreach ( (array) $contributors as $k => $v ) { 304 $v = $this->_sanitize_text( $v ); 305 if ( !empty( $v ) ) 306 $contributors[$k] = sanitize_user( $v ); 307 else 308 unset( $contributors[$k] ); 309 } 310 $records['contributors'] = $contributors; 311 312 // Assign records to object properties 313 foreach ( (array) $records as $k => $v ) 314 if (in_array( $k , array_keys( $this->_keys ) ) ) 315 $this->{"_$k"} = $v; 316 317 // Done 318 return $records; 319 } 320 321 /** 322 * Extract key-value pairs 323 * @return array 324 */ 325 private function _extract_records( ) { 326 $ret = array(); 327 foreach ( (array) $this->_keys as $k => $v ) { 328 if ( preg_match( $v, $this->_readme_contents, $matches ) ) { 329 $ret[$k] = $this->_sanitize_text( $matches[1] ); 330 $this->_readme_contents = trim( str_replace( $matches[0], '', $this->_readme_contents ) ); 331 } 332 } 333 return $this->_process_records( $ret ); 334 } 335 336 /** 337 * Parse a readme file into an associative array 338 * @return string 339 */ 340 protected function _parse_readme() { 341 342 // Normalize white-space 343 $file_contents = str_replace( array( "\r\n", "\r" ), "\n", $this->_readme_contents ); 344 $this->_readme_contents = trim( $this->_readme_contents ); 345 346 // Remove UTF-8 byte-order-mark 347 if ( 0 === strpos( $this->_readme_contents, "\xEF\xBB\xBF" ) ) 348 $this->_readme_contents = substr( $this->_readme_contents, 3 ); 349 350 // === Plugin Name === 351 // Must be the very first thing. 352 if ( !preg_match( '/^===(.*)===/', $this->_readme_contents, $_name ) ) 353 return array(); // require a name 354 $name = trim( $_name[1], '=' ); 355 $name = $this->_sanitize_text( $name ); 356 $this->_readme_contents = str_replace( $_name[0], '', $this->_readme_contents ); 357 358 // Extract the "key: value" records to the local scope 359 extract( $this->_extract_records() ); 360 361 // Extract the sections (e.g. == HEADING == .... text ...) 362 $sections = $this->_exract_sections(); 363 extract( $sections ); 364 365 // Compile the final array 366 return array( 367 'name' => isset( $name ) ? $name : '', 368 'tags' => isset( $tags ) ? $tags : array(), 369 'requires_at_least' => isset( $requires_at_least ) ? $requires_at_least : '', 370 'tested_up_to' => isset( $tested_up_to ) ? $tested_up_to : '', 371 'stable_tag' => isset( $stable_tag ) ? $stable_tag : 'trunk', 372 'contributors' => isset( $contributors ) ? $contributors : array(), 373 'donate_link' => isset( $donate_link ) ? $donate_link : '', 374 'short_description' => $this->_short_description, 375 'screenshots' => $this->_screenshots, 376 'is_excerpt' => $this->_is_excerpt, 377 'is_truncated' => $this->_is_truncated, 378 'sections' => $sections, 379 'remaining_content' => $this->_remaining_content, 380 'upgrade_notice' => $this->_upgrade_notice 381 ); 382 } 383 384 /** 385 * Make the text safe for use in a browser 386 * @param string $text 387 * @return string 388 */ 389 private function _sanitize_text( $text ) { 390 $text = strip_tags( $text ); 391 $text = esc_html( $text ); 392 $text = trim( $text ); 393 return $text; 394 } 395 396 /** 397 * Format text. Use markdown, with some extra code -> backtick formatting. 398 * @param type $text 399 * @return type 400 */ 401 private function _markdown( $text ) { 402 $text = trim( $text ); 403 $text = $this->_convert_code( $text ); 404 $text = Markdown( $text ); 405 $text = balanceTags( $text ); 406 $text = wp_kses( $text, $this->_allowed_tags ); 407 $text = trim( $text ); 408 return $text; 409 } 410 411 /** 412 * First take any user formatted code blocks and turn them into backticks so that 413 * markdown will preserve things like underscores in code blocks 414 * @param type $text 415 * @param type $markdown 416 * @return type 417 */ 418 private function _convert_code( $text ) { 419 $text = preg_replace_callback( '/(<pre><code>|<code>)(.*?)(<\/code>\/pre>|<\/code>)/s', array( $this, '_decodeit' ), $text ); 420 $text = str_replace( array("\r\n", "\r"), "\n", $text ); 421 // Markdown seems to be choking on block level stuff too. Let's just encode it and be done with it. 422 $text = preg_replace_callback( '/(^|\n)`(.*?)`/s', array( $this, '_encodeit' ), $text ); 423 return $text; 424 } 425 426 /** 427 * Encode Markdown to HTML 428 * @param array $matches Output from preg_replace_callback 429 * @return string 430 */ 431 private function _encodeit( $matches ) { 432 $text = trim( $matches[2] ); 433 $text = htmlspecialchars( $text, ENT_QUOTES ); 434 $text = str_replace( array( "\r\n", "\r" ), "\n", $text ); 435 $text = preg_replace( "|\n\n\n+|", "\n\n", $text ); 436 $text = str_replace( 437 array( '&lt;', '&> ' ), 438 array( '<' , '>' ), 439 $text 440 ); 441 $text = "<code>$text</code>"; 442 if ( "`" != $matches[1] ) 443 $text = "<pre>$text</pre>"; 444 return $text; 445 } 446 447 /** 448 * De-code HTML, turn it back into markdown 449 * @param array $matches Output from preg_replace_callback 450 * @return string 451 */ 452 private function _decodeit( $matches ) { 453 $text = $matches[2]; 454 $text = html_entity_decode( $text ); 455 $text = str_replace( 456 array( '<br />', '&', '''), 457 array( '' , '&' , "'" ), 458 $text 459 ); 460 if ( '<pre><code>' == $matches[1] ) 461 $text = "\n$text\n"; 462 return "`$text`"; 463 } 464 } -
wp-admin/includes/class-wp-plugins-list-table.php
Property changes on: wp-admin\includes\class-wp-plugin-readme-parser.php ___________________________________________________________________ Added: svn:eol-style + LF
427 427 $author = '<a href="' . $plugin_data['AuthorURI'] . '" title="' . esc_attr__( 'Visit author homepage' ) . '">' . $plugin_data['Author'] . '</a>'; 428 428 $plugin_meta[] = sprintf( __( 'By %s' ), $author ); 429 429 } 430 $slug = basename( $plugin_file, '.php' ); 431 $plugin_meta[] = '<a href="' . self_admin_url( 'plugin-install.php?tab=plugin-readme&readme=true&plugin=' . $slug . 432 '&TB_iframe=true&width=600&height=550' ) . '" class="thickbox" title="' . 433 esc_attr( sprintf( __( 'More information about %s' ), "{$plugin_data['Name']} {$plugin_data['Version']}" ) ) . '">' . __( 'Details' ) . '</a>'; 430 434 if ( ! empty( $plugin_data['PluginURI'] ) ) 431 435 $plugin_meta[] = '<a href="' . $plugin_data['PluginURI'] . '" title="' . esc_attr__( 'Visit plugin site' ) . '">' . __( 'Visit plugin site' ) . '</a>'; 432 436 -
wp-admin/css/colors-classic.dev.css
583 583 } 584 584 585 585 div#media-upload-header, 586 div#plugin-information-header { 586 div#plugin-information-header, 587 div#plugin-readme-header { 587 588 background-color: #f9f9f9; 588 589 border-bottom-color: #dfdfdf; 589 590 } … … 1725 1726 border-right: 1px solid #fff; 1726 1727 } 1727 1728 1728 #plugin-information .fyi ul { 1729 #plugin-information .fyi ul, 1730 #plugin-readme .fyi ul { 1729 1731 background-color: #eaf3fa; 1730 1732 } 1731 1733 1732 #plugin-information .fyi h2.mainheader { 1734 #plugin-information .fyi h2.mainheader, 1735 #plugin-readme .fyi h2.mainheader { 1733 1736 background-color: #cee1ef; 1734 1737 } 1735 1738 1736 1739 #plugin-information pre, 1737 #plugin-information code { 1740 #plugin-information code, 1741 #plugin-readme pre, 1742 #plugin-readme code { 1738 1743 background-color: #ededff; 1739 1744 } 1740 1745 1741 #plugin-information pre { 1746 #plugin-information pre, 1747 #plugin-readme pre { 1742 1748 border: 1px solid #ccc; 1743 1749 } 1744 1750 -
wp-admin/css/colors-fresh.dev.css
591 591 } 592 592 593 593 div#media-upload-header, 594 div#plugin-information-header { 594 div#plugin-information-header, 595 div#plugin-readme-header { 595 596 background-color: #f9f9f9; 596 597 border-bottom-color: #dfdfdf; 597 598 } … … 1360 1361 border-right: 1px solid #f9f9f9; 1361 1362 } 1362 1363 1363 #plugin-information .fyi ul { 1364 #plugin-information .fyi ul, 1365 #plugin-readme .fyi ul { 1364 1366 background-color: #eaf3fa; 1365 1367 } 1366 1368 1367 #plugin-information .fyi h2.mainheader { 1369 #plugin-information .fyi h2.mainheader, 1370 #plugin-readme .fyi h2.mainheader { 1368 1371 background-color: #cee1ef; 1369 1372 } 1370 1373 1371 1374 #plugin-information pre, 1372 #plugin-information code { 1375 #plugin-information code, 1376 #plugin-readme pre, 1377 #plugin-readme code { 1373 1378 background-color: #ededff; 1374 1379 } 1375 1380 1376 #plugin-information pre { 1381 #plugin-information pre, 1382 #plugin-readme pre { 1377 1383 border: 1px solid #ccc; 1378 1384 } 1379 1385 -
wp-admin/plugin-install.php
6 6 * @subpackage Administration 7 7 */ 8 8 // TODO route this pages via a specific iframe handler instead of the do_action below 9 if ( !defined( 'IFRAME_REQUEST' ) && isset( $_GET['tab'] ) && ( 'plugin-information' == $_GET['tab']) )9 if ( !defined( 'IFRAME_REQUEST' ) && isset( $_GET['tab'] ) && ( in_array( $_GET['tab'], array( 'plugin-information', 'plugin-readme' ) ) ) ) 10 10 define( 'IFRAME_REQUEST', true ); 11 11 12 12 /** WordPress Administration Bootstrap */ … … 28 28 $parent_file = 'plugins.php'; 29 29 30 30 wp_enqueue_script( 'plugin-install' ); 31 if ( 'plugin-information' != $tab)31 if ( !in_array( $tab, array('plugin-information', 'plugin-readme') ) ) 32 32 add_thickbox(); 33 33 34 34 $body_id = $tab; -
wp-admin/includes/plugin-install.php
384 384 exit; 385 385 } 386 386 add_action('install_plugins_pre_plugin-information', 'install_plugin_information'); 387 388 /** 389 * Get information from a plugin locally 390 * @param string $slug 391 * @since 3.4.0 392 */ 393 function local_plugin_api( $slug ) { 394 395 // Try to find the plugin file from the slug 396 $plugin_data = array(); 397 if ( file_exists( ABSPATH . PLUGINDIR . "/$slug.php" ) ) { 398 $plugin_data = get_plugin_data( ABSPATH . PLUGINDIR . "/$slug.php" ); 399 } elseif ( file_exists( ABSPATH . PLUGINDIR . "/$slug/$slug.php" ) ) { 400 $plugin_data = get_plugin_data( ABSPATH . PLUGINDIR . "/$slug/$slug.php" ); 401 } 402 403 // Try to load data from readme.txt 404 $readme_data = false; 405 if ( file_exists( ABSPATH . PLUGINDIR . "/$slug/readme.txt" ) ) { 406 407 // Get markddown library, turn off plugin functionality 408 if ( !defined( 'MARKDOWN_WP_POSTS' ) ) 409 define( 'MARKDOWN_WP_POSTS', false ); 410 if ( !defined( 'MARKDOWN_WP_COMMENTS' ) ) 411 define( 'MARKDOWN_WP_COMMENTS', false ); 412 if ( !class_exists( 'Markdown_Parser' ) ) 413 include_once( ABSPATH . 'wp-admin/includes/markdown.php' ); 414 415 include_once( ABSPATH . 'wp-admin/includes/class-wp-plugin-readme-parser.php' ); 416 $readme_parser = new wp_plugin_readme_parser(); 417 $readme_data = $readme_parser->parse_readme_file( ABSPATH . PLUGINDIR . "/$slug/readme.txt" ); 418 } 419 420 // If there's no readme (e.g. hello.php) create a fake structure 421 if ( empty( $readme_data ) ) { 422 $readme_data = array( 423 'contributors' => '', 424 'requires_at_least' => '', 425 'tested_up_to' => '', 426 'tags' => array(), 427 'sections' => array( 428 'description' => !empty( $plugin_data['Description'] ) ? $plugin_data['Description'] : '' 429 ) 430 ); 431 } 432 433 // Convert to an API-style response 434 $api = array( 435 'name' => !empty( $plugin_data['Name'] ) ? $plugin_data['Name'] : $slug, 436 'slug' => $slug, 437 'version' => !empty( $plugin_data['Version'] ) ? $plugin_data['Version'] : '', 438 'author' => !empty( $plugin_data['AuthorName'] ) ? $plugin_data['AuthorName'] : '', 439 'author_profile' => null, 440 'contributors' => $readme_data['contributors'], 441 'requires' => $readme_data['requires_at_least'], 442 'tested' => $readme_data['tested_up_to'], 443 'compatibility' => null, 444 'rating' => null, 445 'num_ratings' => null, 446 'downloaded' => null, 447 'last_updated' => null, 448 'added' => null, 449 'homepage' => !empty( $plugin_data['PluginURI'] ) ? $plugin_data['PluginURI'] : '', 450 'sections' => $readme_data['sections'], 451 'download_link' => null, 452 'tags' => $readme_data['tags'] 453 ); 454 455 // Done 456 return (object) $api; 457 } 458 459 /** 460 * Display local plugin information in dialog box form. 461 * Pull from the readme.txt file and the plugin header 462 * @since 3.4.0 463 */ 464 function plugin_readme_information() { 465 global $tab; 466 467 $api = local_plugin_api( stripslashes( $_REQUEST['plugin'] ) ); 468 469 if ( is_wp_error($api) ) 470 wp_die($api); 471 472 $plugins_allowedtags = array( 473 'a' => array( 474 'href' => array(), 475 'title' => array(), 476 'target' => array() 477 ), 478 'abbr' => array( 479 'title' => array() 480 ), 481 'acronym' => array( 482 'title' => array() 483 ), 484 'code' => array(), 485 'pre' => array(), 486 'em' => array(), 487 'strong' => array(), 488 'div' => array(), 489 'p' => array(), 490 'ul' => array(), 491 'ol' => array(), 492 'li' => array(), 493 'h1' => array(), 494 'h2' => array(), 495 'h3' => array(), 496 'h4' => array(), 497 'h5' => array(), 498 'h6' => array(), 499 'img' => array( 500 'src' => array(), 501 'class' => array(), 502 'alt' => array() 503 ) 504 ); 505 506 $plugins_section_titles = array( 507 'description' => _x('Description', 'Plugin installer section title'), 508 'installation' => _x('Installation', 'Plugin installer section title'), 509 'faq' => _x('FAQ', 'Plugin installer section title'), 510 'changelog' => _x('Changelog', 'Plugin installer section title'), 511 'other_notes' => _x('Other Notes', 'Plugin installer section title') 512 ); 513 514 // No screenshots at this time 515 if ( !empty( $api->sections['screenshots'] ) ) 516 unset( $api->sections['screenshots'] ); 517 518 // Sanitize HTML 519 foreach ( (array)$api->sections as $section_name => $content ) 520 $api->sections[$section_name] = wp_kses( $content, $plugins_allowedtags ); 521 foreach ( array( 'version', 'author', 'requires', 'tested', 'homepage', 'downloaded', 'slug' ) as $key ) { 522 if ( isset( $api->$key ) ) 523 $api->$key = wp_kses( $api->$key, $plugins_allowedtags ); 524 } 525 526 // Default to the Description tab, Do not translate, API returns English. 527 $section = isset( $_REQUEST['section'] ) ? stripslashes( $_REQUEST['section'] ) : 'description'; 528 if ( empty($section) || ! isset($api->sections[ $section ]) ) 529 $section = array_shift( $section_titles = array_keys((array)$api->sections) ); 530 531 iframe_header( __( 'Plugin Details' ) ); 532 ?> 533 <div id="<?php echo $tab; ?>-header"> 534 <ul id="sidemenu"> 535 <?php foreach ( (array) $api->sections as $section_name => $content ) : ?> 536 <?php 537 if ( isset( $plugins_section_titles[ $section_name ] ) ) 538 $title = $plugins_section_titles[ $section_name ]; 539 else 540 $title = ucwords( str_replace( '_', ' ', $section_name ) ); 541 542 $class = ( $section_name == $section ) ? ' class="current"' : ''; 543 $href = add_query_arg( array('tab' => $tab, 'section' => $section_name) ); 544 $href = esc_url($href); 545 $san_section = esc_attr( $section_name ); 546 ?> 547 <li><a name="<?php echo $san_section; ?>" href="<?php echo $href; ?>" <?php echo $class; ?>><?php echo $title; ?></a></li> 548 <?php endforeach ; ?> 549 </ul> 550 </div> 551 <div class="alignright fyi"> 552 <h2 class="mainheader"><?php /* translators: For Your Information */ _e('FYI') ?></h2> 553 <ul> 554 <?php if ( !empty( $api->version ) ) : ?> 555 <li><strong><?php _e('Version:') ?></strong> <?php echo $api->version ?></li> 556 <?php endif; ?> 557 <?php if ( !empty( $api->author ) ) : ?> 558 <li><strong><?php _e('Author:') ?></strong> <?php echo $api->author ?></li> 559 <?php endif; ?> 560 <?php if ( !empty( $api->requires ) ) : ?> 561 <li><strong><?php _e('Requires WordPress Version:') ?></strong> <?php printf(__('%s or higher'), $api->requires) ?></li> 562 <?php endif; ?> 563 <?php if ( !empty( $api->tested ) ) : ?> 564 <li><strong><?php _e('Compatible up to:') ?></strong> <?php echo $api->tested ?></li> 565 <?php endif; ?> 566 <?php if ( !empty( $api->homepage ) ) : ?> 567 <li><a target="_blank" href="<?php echo $api->homepage ?>"><?php _e('Plugin Homepage »') ?></a></li> 568 <?php endif; ?> 569 </ul> 570 </div> 571 <div id="section-holder" class="wrap"> 572 <?php 573 if ( !empty( $api->tested ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->tested ) ), $api->tested, '>' ) ) 574 echo '<div class="updated"><p>' . __('<strong>Warning:</strong> This plugin has <strong>not been tested</strong> with your current version of WordPress.') . '</p></div>'; 575 576 elseif ( !empty( $api->requires ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->requires ) ), $api->requires, '<' ) ) 577 echo '<div class="updated"><p>' . __('<strong>Warning:</strong> This plugin has <strong>not been marked as compatible</strong> with your version of WordPress.') . '</p></div>'; 578 579 foreach ( (array) $api->sections as $section_name => $content ) { 580 if ( isset( $plugins_section_titles[ $section_name ] ) ) 581 $title = $plugins_section_titles[ $section_name ]; 582 else 583 $title = ucwords( str_replace( '_', ' ', $section_name ) ); 584 585 $content = links_add_target($content, '_blank'); 586 $san_section = esc_attr( $section_name ); 587 $display = ( $section_name == $section ) ? 'block' : 'none'; 588 ?> 589 <div id="section-<?php echo $san_section; ?>" class="section" style="display: <?php echo $display; ?>;"> 590 <h2 class="long-header"><?php echo $title; ?></h2> 591 <?php echo $content; ?> 592 </div> 593 <?php 594 } 595 echo "</div>\n"; 596 iframe_footer(); 597 exit; 598 } 599 add_action('install_plugins_pre_plugin-readme', 'plugin_readme_information'); -
wp-admin/css/wp-admin-rtl.dev.css
2148 2148 left: 0; 2149 2149 } 2150 2150 2151 #plugin-information ul#sidemenu { 2151 #plugin-information ul#sidemenu, 2152 #plugin-readme ul#sidemenu { 2152 2153 left: auto; 2153 2154 right: 0; 2154 2155 } 2155 2156 2156 #plugin-information h2 { 2157 #plugin-information h2, 2158 #plugin-readme h2 { 2157 2159 margin-right: 0; 2158 2160 margin-left: 200px; 2159 2161 } 2160 2162 2161 #plugin-information .fyi { 2163 #plugin-information .fyi, 2164 #plugin-readme .fyi { 2162 2165 margin-left: 5px; 2163 2166 margin-right: 20px; 2164 2167 } 2165 2168 2166 #plugin-information .fyi h2 { 2169 #plugin-information .fyi h2, 2170 #plugin-readme .fyi h2 { 2167 2171 margin-left: 0; 2168 2172 } 2169 2173 2170 #plugin-information .fyi ul { 2174 #plugin-information .fyi ul, 2175 #plugin-readme .fyi ul { 2171 2176 padding: 10px 7px 10px 5px; 2172 2177 } 2173 2178 … … 2178 2183 2179 2184 #plugin-information #section-screenshots ol, 2180 2185 #plugin-information .updated, 2181 #plugin-information pre { 2186 #plugin-information pre, 2187 #plugin-readme .updated, 2188 #plugin-readme pre { 2182 2189 margin-right: 0; 2183 2190 margin-left: 215px; 2184 2191 } 2185 2192 2186 2193 #plugin-information .updated, 2187 #plugin-information .error { 2194 #plugin-information .error, 2195 #plugin-readme .updated, 2196 #plugin-readme .error { 2188 2197 clear: none; 2189 2198 direction: rtl; 2190 2199 } -
wp-admin/css/wp-admin.dev.css
6777 6777 } 6778 6778 6779 6779 /* Header on thickbox */ 6780 #plugin-information-header { 6780 #plugin-information-header, 6781 #plugin-readme-header { 6781 6782 margin: 0; 6782 6783 padding: 0 5px; 6783 6784 font-weight: bold; … … 6786 6787 border-bottom-style: solid; 6787 6788 height: 2.5em; 6788 6789 } 6789 #plugin-information ul#sidemenu { 6790 #plugin-information ul#sidemenu, 6791 #plugin-readme ul#sidemenu { 6790 6792 font-weight: normal; 6791 6793 margin: 0 5px; 6792 6794 position: absolute; … … 6814 6816 line-height: 2em; 6815 6817 } 6816 6818 6817 #plugin-information h2 { 6819 #plugin-information h2, 6820 #plugin-readme h2 { 6818 6821 clear: none !important; 6819 6822 margin-right: 200px; 6820 6823 } 6821 6824 6822 #plugin-information .fyi { 6825 #plugin-information .fyi, 6826 #plugin-readme .fyi { 6823 6827 margin: 0 10px 50px; 6824 6828 width: 210px; 6825 6829 } 6826 6830 6827 #plugin-information .fyi h2 { 6831 #plugin-information .fyi h2, 6832 #plugin-readme .fyi h2 { 6828 6833 font-size: 0.9em; 6829 6834 margin-bottom: 0; 6830 6835 margin-right: 0; 6831 6836 } 6832 6837 6833 #plugin-information .fyi h2.mainheader { 6838 #plugin-information .fyi h2.mainheader, 6839 #plugin-readme .fyi h2.mainheader { 6834 6840 padding: 5px; 6835 6841 -webkit-border-top-left-radius: 3px; 6836 6842 border-top-left-radius: 3px; 6837 6843 } 6838 6844 6845 #plugin-readme .fyi ul, 6839 6846 #plugin-information .fyi ul { 6840 6847 padding: 10px 5px 10px 7px; 6841 6848 margin: 0; … … 6844 6851 border-bottom-left-radius: 3px; 6845 6852 } 6846 6853 6847 #plugin-information .fyi li { 6854 #plugin-information .fyi li, 6855 #plugin-readme .fyi li { 6848 6856 margin-right: 0; 6849 6857 } 6850 6858 6851 #plugin-information #section-holder { 6859 #plugin-information #section-holder, 6860 #plugin-readme #section-holder { 6852 6861 padding: 10px; 6853 6862 } 6854 6863 6855 6864 #plugin-information .section ul, 6856 #plugin-information .section ol { 6865 #plugin-information .section ol, 6866 #plugin-readme .section ul, 6867 #plugin-readme .section ol { 6857 6868 margin-left: 16px; 6858 6869 list-style-type: square; 6859 6870 list-style-image: none; … … 6879 6890 6880 6891 #plugin-information #section-screenshots ol, 6881 6892 #plugin-information .updated, 6882 #plugin-information pre { 6893 #plugin-information pre, 6894 #plugin-readme .updated, 6895 #plugin-readme pre { 6883 6896 margin-right: 215px; 6884 6897 } 6885 6898 6886 #plugin-information pre { 6899 #plugin-information pre, 6900 #plugin-readme pre { 6887 6901 padding: 7px; 6888 6902 overflow: auto; 6889 6903 }