From e102dcb32a2541eb81ece2e9f467215858ea3ff8 Mon Sep 17 00:00:00 2001 From: Andy Fragen Date: Thu, 14 Jul 2016 10:28:28 -0700 Subject: [PATCH] switch to rmccue's readme parser fixed rmccue's parser to recognize markdownified readme --- github-updater.php | 10 +- src/GitHub_Updater/Base.php | 4 +- src/GitHub_Updater/Readme_Parser.php | 55 +--- vendor/ReadmeParser.php | 370 +++++++++++++++++++++++++++ 4 files changed, 388 insertions(+), 51 deletions(-) create mode 100755 vendor/ReadmeParser.php diff --git a/github-updater.php b/github-updater.php index 89d3969e2..5e62aace6 100644 --- a/github-updater.php +++ b/github-updater.php @@ -34,7 +34,7 @@ } if ( ! class_exists( 'WPUpdatePhp' ) ) { - require_once ( plugin_dir_path( __FILE__ ) . '/vendor/wp-update-php/src/WPUpdatePhp.php' ); + require_once( plugin_dir_path( __FILE__ ) . '/vendor/wp-update-php/src/WPUpdatePhp.php' ); } $updatePhp = new WPUpdatePhp( '5.3.0' ); if ( method_exists( $updatePhp, 'set_plugin_name' ) ) { @@ -52,10 +52,10 @@ // Add extra classes $extra_classes = array( - 'Parsedown' => __DIR__ . '/vendor/parsedown/Parsedown.php', - 'WPUpdatePHP' => __DIR__ . '/vendor/wp-update-php/src/WPUpdatePhp.php', - 'Automattic_Readme' => __DIR__ . '/vendor/parse-readme.php', - ); + 'Parsedown' => __DIR__ . '/vendor/parsedown/Parsedown.php', + 'WPUpdatePHP' => __DIR__ . '/vendor/wp-update-php/src/WPUpdatePhp.php', + 'Baikonur_ReadmeParser' => __DIR__ . '/vendor/ReadmeParser.php', +); // Load Autoloader require_once( __DIR__ . '/src/GitHub_Updater/Autoloader.php' ); diff --git a/src/GitHub_Updater/Base.php b/src/GitHub_Updater/Base.php index 741e19b25..da0f9d99f 100644 --- a/src/GitHub_Updater/Base.php +++ b/src/GitHub_Updater/Base.php @@ -911,8 +911,8 @@ protected function set_readme_info( $response ) { unset( $response['sections']['screenshots'] ); unset( $response['sections']['installation'] ); $this->type->sections = array_merge( (array) $this->type->sections, (array) $response['sections'] ); - $this->type->tested = $response['tested_up_to']; - $this->type->requires = $response['requires_at_least']; + $this->type->tested = $response['tested']; + $this->type->requires = $response['requires']; $this->type->donate_link = $response['donate_link']; $this->type->contributors = $response['contributors']; diff --git a/src/GitHub_Updater/Readme_Parser.php b/src/GitHub_Updater/Readme_Parser.php index ab07a72ec..2628d7185 100644 --- a/src/GitHub_Updater/Readme_Parser.php +++ b/src/GitHub_Updater/Readme_Parser.php @@ -22,7 +22,7 @@ * * @package Fragen\GitHub_Updater */ -class Readme_Parser extends \Automattic_Readme { +class Readme_Parser extends \Baikonur_ReadmeParser { /** * Constructor @@ -35,55 +35,22 @@ public function __construct() { * * @return array */ - public function parse_readme( $file_contents ) { - return (array) $this->parse_readme_contents( $file_contents ); + public static function parse_readme( $file_contents ) { + return (array) parent::parse_readme_contents( $file_contents ); } /** - * @param $text - * @param bool $markdown + * @param $text * - * @return mixed|string + * @return string */ - public function filter_text( $text, $markdown = false ) { // fancy, Markdown - $text = trim( $text ); - $text = call_user_func( array( - get_parent_class( $this ), - 'code_trick', - ), $text, $markdown ); // A better parser than Markdown's for: backticks -> CODE + protected static function parse_markdown( $text ) { + $parser = new \Parsedown(); + $text = parent::code_trick( $text ); + $text = preg_replace( '/^[\s]*=[\s]+(.+?)[\s]+=/m', "\n" . '

$1

' . "\n", $text ); + $text = $parser->text( trim( $text ) ); - if ( $markdown ) { // Parse markdown. - $parser = new \Parsedown; - $text = $parser->text( $text ); - } - - $allowed = array( - 'a' => array( - 'href' => array(), - 'title' => array(), - 'rel' => array(), - ), - 'blockquote' => array( 'cite' => array() ), - 'br' => array(), - 'cite' => array(), - 'p' => array(), - 'code' => array(), - 'pre' => array(), - 'em' => array(), - 'strong' => array(), - 'ul' => array(), - 'ol' => array(), - 'li' => array(), - 'h3' => array(), - 'h4' => array(), - ); - - $text = balanceTags( $text ); - - $text = wp_kses( $text, $allowed ); - $text = trim( $text ); - - return $text; + return trim( $text ); } } diff --git a/vendor/ReadmeParser.php b/vendor/ReadmeParser.php new file mode 100755 index 000000000..54f2a3ded --- /dev/null +++ b/vendor/ReadmeParser.php @@ -0,0 +1,370 @@ +is_excerpt = false; + $data->is_truncated = false; + $data->tags = array(); + $data->requires = ''; + $data->tested = ''; + $data->contributors = array(); + $data->stable_tag = ''; + $data->version = ''; + $data->donate_link = ''; + $data->short_description = ''; + $data->sections = array(); + $data->changelog = array(); + $data->upgrade_notice = array(); + $data->screenshots = array(); + $data->remaining_content = array(); + + $line = call_user_func_array( array( $this_class, 'get_first_nonwhitespace' ), array( &$contents ) ); + $data->name = $line; + $data->name = trim( $data->name, "#= " ); + + // Parse headers + $headers = array(); + + $line = call_user_func_array( array( $this_class, 'get_first_nonwhitespace' ), array( &$contents ) ); + do { + $key = $value = null; + if ( strpos( $line, ':' ) === false ) { + break; + } + $bits = explode( ':', $line, 2 ); + list( $key, $value ) = $bits; + $key = strtolower( str_replace( array( ' ', "\t" ), '_', trim( $key ) ) ); + if ( $key === 'tags' && isset( $headers['tags'] ) ) { + $headers[ $key ] .= ',' . trim( $value ); + } else { + $headers[ $key ] = trim( $value ); + } + } while ( ( $line = array_shift( $contents ) ) !== null && ( $line = trim( $line ) ) && ! empty( $line ) ); + array_unshift( $contents, $line ); + + if ( ! empty( $headers['tags'] ) ) { + $data->tags = explode( ',', $headers['tags'] ); + $data->tags = array_map( 'trim', $data->tags ); + } + if ( ! empty( $headers['requires'] ) ) { + $data->requires = $headers['requires']; + } + if ( ! empty( $headers['requires_at_least'] ) ) { + $data->requires = $headers['requires_at_least']; + } + if ( ! empty( $headers['tested'] ) ) { + $data->tested = $headers['tested']; + } + if ( ! empty( $headers['tested_up_to'] ) ) { + $data->tested = $headers['tested_up_to']; + } + if ( ! empty( $headers['contributors'] ) ) { + $data->contributors = explode( ',', $headers['contributors'] ); + $data->contributors = array_map( 'trim', $data->contributors ); + } + if ( ! empty( $headers['stable_tag'] ) ) { + $data->stable_tag = $headers['stable_tag']; + } + if ( ! empty( $headers['donate_link'] ) ) { + $data->donate_link = $headers['donate_link']; + } + if ( ! empty( $headers['version'] ) ) { + $data->version = $headers['version']; + } else { + $data->version = $data->stable_tag; + } + + // Parse the short description + while ( ( $line = array_shift( $contents ) ) !== null ) { + $trimmed = trim( $line ); + if ( empty( $trimmed ) ) { + $data->short_description .= "\n"; + continue; + } + if ( ( $trimmed[0] === '=' && isset( $trimmed[1] ) && $trimmed[1] === '=' ) || + 1 === preg_match( '/^##[ ]+/', $line ) + ) { + array_unshift( $contents, $line ); + break; + } + + $data->short_description .= $line . "\n"; + } + $data->short_description = trim( $data->short_description ); + + $data->is_truncated = call_user_func_array( array( + $this_class, + 'trim_short_desc', + ), array( &$data->short_description ) ); + + // Parse the rest of the body + $current = ''; + $special = array( + 'description', + 'installation', + 'faq', + 'frequently_asked_questions', + 'screenshots', + 'changelog', + 'upgrade_notice', + ); + + while ( ( $line = array_shift( $contents ) ) !== null ) { + $trimmed = trim( $line ); + if ( empty( $trimmed ) ) { + $current .= "\n"; + continue; + } + + if ( ( $trimmed[0] === '=' && isset( $trimmed[1] ) && $trimmed[1] === '=' ) || + 1 === preg_match( '/^##[ ]+/', $line ) + ) { + if ( ! empty( $title ) ) { + $data->sections[ $title ] = trim( $current ); + } + + $current = ''; + $real_title = trim( $line, "#= \t" ); + $title = strtolower( str_replace( ' ', '_', $real_title ) ); + if ( $title === 'faq' ) { + $title = 'frequently_asked_questions'; + } elseif ( $title === 'change_log' ) { + $title = 'changelog'; + } + if ( ! in_array( $title, $special ) ) { + $current .= '

' . $real_title . "

"; + } + continue; + } + + $current .= $line . "\n"; + } + + if ( ! empty( $title ) ) { + $data->sections[ $title ] = trim( $current ); + } + $title = null; + $current = null; + + if ( empty( $data->sections['description'] ) ) { + $data->sections['description'] = call_user_func( array( + $this_class, + 'parse_markdown', + ), $data->short_description ); + } + + // Parse changelog + if ( ! empty( $data->sections['changelog'] ) ) { + $lines = explode( "\n", $data->sections['changelog'] ); + while ( ( $line = array_shift( $lines ) ) !== null ) { + $trimmed = trim( $line ); + if ( empty( $trimmed ) ) { + continue; + } + + if ( $trimmed[0] === '=' ) { + if ( ! empty( $current ) ) { + $data->changelog[ $title ] = trim( $current ); + } + + $current = ''; + $title = trim( $line, "#= \t" ); + continue; + } + + $current .= $line . "\n"; + } + + $data->changelog[ $title ] = trim( $current ); + } + $title = null; + $current = null; + + if ( isset( $data->sections['upgrade_notice'] ) ) { + $lines = explode( "\n", $data->sections['upgrade_notice'] ); + while ( ( $line = array_shift( $lines ) ) !== null ) { + $trimmed = trim( $line ); + if ( empty( $trimmed ) ) { + continue; + } + + if ( $trimmed[0] === '=' ) { + if ( ! empty( $current ) ) { + $data->upgrade_notice[ $title ] = trim( $current ); + } + + $current = ''; + $title = trim( $line, "#= \t" ); + continue; + } + + $current .= $line . "\n"; + } + + if ( ! empty( $title ) && ! empty( $current ) ) { + $data->upgrade_notice[ $title ] = trim( $current ); + } + unset( $data->sections['upgrade_notice'] ); + } + + // Markdownify! + + $data->sections = array_map( array( $this_class, 'parse_markdown' ), $data->sections ); + $data->changelog = array_map( array( $this_class, 'parse_markdown' ), $data->changelog ); + $data->upgrade_notice = array_map( array( $this_class, 'parse_markdown' ), $data->upgrade_notice ); + + if ( isset( $data->sections['screenshots'] ) ) { + preg_match_all( '#
  • (.*?)
  • #is', $data->sections['screenshots'], $screenshots, PREG_SET_ORDER ); + if ( $screenshots ) { + foreach ( (array) $screenshots as $ss ) { + $data->screenshots[] = trim( $ss[1] ); + } + } + } + + // Rearrange stuff + + $data->remaining_content = $data->sections; + $data->sections = array(); + + foreach ( $special as $spec ) { + if ( isset( $data->remaining_content[ $spec ] ) ) { + $data->sections[ $spec ] = $data->remaining_content[ $spec ]; + unset( $data->remaining_content[ $spec ] ); + } + } + + $data->remaining_content = implode( "\n", $data->remaining_content ); + + return $data; + } + + protected static function get_first_nonwhitespace( &$contents ) { + while ( ( $line = array_shift( $contents ) ) !== null ) { + $trimmed = trim( $line ); + if ( ! empty( $line ) ) { + break; + } + } + + return $line; + } + + protected static function strip_newlines( $line ) { + return rtrim( $line, "\r\n" ); + } + + protected static function trim_short_desc( &$desc ) { + if ( function_exists( 'mb_strlen' ) && function_exists( 'mb_substr' ) ) { + if ( mb_strlen( $desc ) > 150 ) { + $desc = mb_substr( $desc, 0, 150 ); + $desc = trim( $desc ); + + return true; + } + } else { + if ( strlen( $desc ) > 150 ) { + $desc = substr( $desc, 0, 150 ); + $desc = trim( $desc ); + + return true; + } + } + + return false; + } + + protected static function parse_markdown( $text ) { + $text = self::code_trick( $text ); + $text = preg_replace( '/^[\s]*=[\s]+(.+?)[\s]+=/m', "\n" . '

    $1

    ' . "\n", $text ); + $text = Markdown( trim( $text ) ); + + return trim( $text ); + } + + protected static function code_trick( $text ) { + // If doing markdown, first take any user formatted code blocks and turn them into backticks so that + // markdown will preserve things like underscores in code blocks + $text = preg_replace_callback( "!(
    |)(.*?)(
    |)!s", array( + __CLASS__, + 'decodeit', + ), $text ); + + $text = str_replace( array( "\r\n", "\r" ), "\n", $text ); + // Markdown can do inline code, we convert bbPress style block level code to Markdown style + $text = preg_replace_callback( "!(^|\n)([ \t]*?)`(.*?)`!s", array( __CLASS__, 'indent' ), $text ); + + return $text; + } + + protected static function indent( $matches ) { + $text = $matches[3]; + $text = preg_replace( '|^|m', $matches[2] . ' ', $text ); + + return $matches[1] . $text; + } + + protected static function decodeit( $matches ) { + $text = $matches[2]; + $trans_table = array_flip( get_html_translation_table( HTML_ENTITIES ) ); + $text = strtr( $text, $trans_table ); + $text = str_replace( '
    ', '', $text ); + $text = str_replace( '&', '&', $text ); + $text = str_replace( ''', "'", $text ); + if ( '
    ' == $matches[1] ) {
    +			$text = "\n$text\n";
    +		}
    +
    +		return "`$text`";
    +	}
    +}