<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[3827] sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/readme/class-parser.php: Plugin Directory: Readme parsing: When parsing the FAQ section, pull out any properly formatted (and some not-so-well-formatted) Question & Answer pairs to be displayed in a formatted fashion.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="http://meta.trac.wordpress.org/changeset/3827">3827</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://meta.trac.wordpress.org/changeset/3827","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>dd32</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2016-08-18 12:12:09 +0000 (Thu, 18 Aug 2016)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Plugin Directory: Readme parsing: When parsing the FAQ section, pull out any properly formatted (and some not-so-well-formatted) Question & Answer pairs to be displayed in a formatted fashion.

The primary method we support for FAQ entries is where the sub headings are the question, with the content following as the answer. Both `=` and `#` are supported for this: 
{{{
=== FAQ ===
== Does the plugin do X? ==
Yes!
## What about Y?
Nah, Sorry!
}}}

As plugins have a wide range of contents at present, we also support plugins which have used bolded headingslike the following, note, that it must start and end the line with two asterisks.
This format isn't suggested for future usage, and is only included for the thousand or more common plugins which have used it.
{{{
=== FAQ ===
** Can I contribute to the plugin? **
Yes! Patches Welcome!
}}}

Finally, there's the plugin readme's who don't confirm to any of the above standards, or have included a paragraph of data prior to their Q&A's, for those plugins the freeform content will be prepended to the FAQ section and they won't get a properly formatted accordian-style interface.

Props obenland for the initial patch, a lot of subtle changes have been made to better handle the various readme's out there (and probably more tweaks are needed).

See <a href="http://meta.trac.wordpress.org/ticket/1810">#1810</a></pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsplugindirectoryreadmeclassparserphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/readme/class-parser.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsplugindirectoryreadmeclassparserphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/readme/class-parser.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/readme/class-parser.php 2016-08-18 02:57:57 UTC (rev 3826)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/readme/class-parser.php   2016-08-18 12:12:09 UTC (rev 3827)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -67,6 +67,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public $screenshots = array();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @var array
+        */
+       public $faq = array();
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * These are the readme sections that we expect.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @var array
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -286,36 +291,21 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Parse out the Upgrade Notice section into it's own data.
</span><span class="cx" style="display: block; padding: 0 10px">                if ( isset( $this->sections['upgrade_notice'] ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $lines   = explode( "\n", $this->sections['upgrade_notice'] );
-                       $version = null;
-                       $current = '';
-                       while ( ( $line = array_shift( $lines ) ) !== null ) {
-                               $trimmed = trim( $line );
-                               if ( empty( $trimmed ) ) {
-                                       continue;
-                               }
-
-                               if ( '=' === $trimmed[0] || '#' === $trimmed[0] ) {
-                                       if ( ! empty( $current ) ) {
-                                               $this->upgrade_notice[ $version ] = $this->sanitize_text( trim( $current ) );
-                                       }
-
-                                       $current = '';
-                                       $version = trim( $line, "#= \t" );
-                                       continue;
-                               }
-
-                               $current .= $line . "\n";
-                       }
-                       if ( ! empty( $version ) && ! empty( $current ) ) {
-                               $this->upgrade_notice[ $version ] = $this->sanitize_text( trim( $current ) );
-                       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $this->upgrade_notice = $this->parse_section( $this->sections['upgrade_notice'] );
+                       $this->upgrade_notice = array_map( array( $this, 'sanitize_text' ), $this->upgrade_notice );
</ins><span class="cx" style="display: block; padding: 0 10px">                         unset( $this->sections['upgrade_notice'] );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Display FAQs as a definition list.
+               if ( isset( $this->sections['faq'] ) ) {
+                       $this->faq = $this->parse_section( $this->sections['faq'] );
+                       $this->sections['faq'] = '';
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 // Markdownify!
</span><span class="cx" style="display: block; padding: 0 10px">                $this->sections       = array_map( array( $this, 'parse_markdown' ), $this->sections );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->upgrade_notice = array_map( array( $this, 'parse_markdown' ), $this->upgrade_notice );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $this->faq            = array_map( array( $this, 'parse_markdown' ), $this->faq );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Sanitize and trim the short_description to match requirements.
</span><span class="cx" style="display: block; padding: 0 10px">                $this->short_description = $this->sanitize_text( $this->short_description );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -334,6 +324,22 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        unset( $this->sections['screenshots'] );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                if ( ! empty( $this->faq ) ) {
+                       // If the FAQ contained data we couldn't parse, we'll treat it as freeform and display it before any questions which are found.
+                       if ( isset( $this->faq[''] ) ) {
+                               $this->sections['faq'] .= $this->faq[''];
+                               unset( $this->faq[''] );
+                       }
+
+                       if ( $this->faq ) {
+                               $this->sections['faq'] .= "\n<dl>\n";
+                               foreach ( $this->faq as $question => $answer ) {
+                                       $this->sections['faq'] .= "<dt>{$question}</dt>\n<dd>{$answer}</dd>\n";
+                               }
+                               $this->sections['faq'] .= "\n</dl>\n";
+                       }
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 // Filter the HTML.
</span><span class="cx" style="display: block; padding: 0 10px">                $this->sections = array_map( array( $this, 'filter_text' ), $this->sections );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -413,6 +419,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'strong'     => true,
</span><span class="cx" style="display: block; padding: 0 10px">                        'ul'         => true,
</span><span class="cx" style="display: block; padding: 0 10px">                        'ol'         => true,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'dl'         => true,
+                       'dt'         => true,
+                       'dd'         => true,
</ins><span class="cx" style="display: block; padding: 0 10px">                         'li'         => true,
</span><span class="cx" style="display: block; padding: 0 10px">                        'h3'         => true,
</span><span class="cx" style="display: block; padding: 0 10px">                        'h4'         => true,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -495,6 +504,52 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Parses a slice of lines from the file into an array of Heading => Content.
+        *
+        * We assume that every heading encountered is a new item, and not a sub heading.
+        * We support headings which are either `= Heading`, `# Heading` or `** Heading`.
+        *
+        * @param string|array $lines The lines of the section to parse.
+        * @return array
+        */
+       protected function parse_section( $lines ) {
+               $key = $value = '';
+               $return = array();
+
+               if ( ! is_array( $lines ) ) {
+                       $lines = explode( "\n", $lines );
+               }
+
+               while ( ( $line = array_shift( $lines ) ) !== null ) {
+                       $trimmed = trim( $line );
+                       if ( ! $trimmed ) {
+                               $value .= "\n";
+                               continue;
+                       }
+
+                       // Normal headings (##.. == ... ==) are matched if they exist, Bold is only used if it starts and ends the line.
+                       if ( $trimmed[0] == '#' || $trimmed[0] == '=' || ( substr( $trimmed, 0, 2 ) == '**' && substr( $trimmed, -2 ) == '**' ) ) {
+                               if ( $value ) {
+                                       $return[ $key ] = trim( $value );
+                               }
+
+                               $value = '';
+                               // Trim off the first character of the line, as we know that's the heading style we're expecting to remove.
+                               $key   = trim( $line, $trimmed[0] . " \t" );
+                               continue;
+                       }
+
+                       $value .= $line . "\n";
+               }
+
+               if ( $key || $value ) {
+                       $return[ $key ] = trim( $value );
+               }
+
+               return $return;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * @param string $text
</span><span class="cx" style="display: block; padding: 0 10px">         * @return string
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span></span></pre>
</div>
</div>

</body>
</html>