ParserOutput: Add stateless transforms to getText()
authorBrad Jorsch <bjorsch@wikimedia.org>
Wed, 22 Nov 2017 18:12:23 +0000 (13:12 -0500)
committerBrad Jorsch <bjorsch@wikimedia.org>
Thu, 30 Nov 2017 19:27:46 +0000 (14:27 -0500)
The stateful transforms are deprecated.

Inspired by Krinkle's If2fb32fc.

Bug: T171797
Change-Id: Ied5fe1a6159c2d4fa48170042b44d735ce7b6f9b

RELEASE-NOTES-1.31
docs/hooks.txt
includes/parser/ParserOptions.php
includes/parser/ParserOutput.php
tests/phpunit/includes/parser/ParserOutputTest.php

index b32e3e7..139773b 100644 (file)
@@ -31,6 +31,8 @@ production.
   [[iw:User:Example|iw>Example]].
 * (T111605) The 'ImportHandleUnknownUser' hook allows extensions to auto-create
   users during an import.
+* Added a hook, ParserOutputPostCacheTransform, to allow extensions to affect
+  the ParserOutput::getText() post-cache transformations.
 
 === External library changes in 1.31 ===
 
@@ -119,6 +121,18 @@ changes to languages because of Phabricator reports.
 * The Block class will no longer accept usable-but-missing usernames for
   'byText' or ->setBlocker(). Callers should either ensure the blocker exists
   locally or use a new interwiki-format username like "iw>Example".
+* The following methods that get and set ParserOutput state are deprecated.
+  Callers should use the new stateless $options parameter to
+  ParserOutput::getText() instead.
+  * ParserOptions::getEditSection()
+  * ParserOptions::setEditSection()
+  * ParserOutput::getEditSectionTokens()
+  * ParserOutput::setEditSectionTokens()
+  * ParserOutput::getTOCEnabled()
+  * ParserOutput::setTOCEnabled()
+  * OutputPage::enableSectionEditLinks()
+  * OutputPage::sectionEditLinksEnabled()
+  * The public ParserOutput state fields $mTOCEnabled and $mEditSectionTokens are also deprecated.
 
 == Compatibility ==
 MediaWiki 1.31 requires PHP 5.5.9 or later. There is experimental support for
index 685a182..29883b2 100644 (file)
@@ -2594,6 +2594,12 @@ RejectParserCacheValue hook) because MediaWiki won't do it for you.
   callable here. The callable is passed the ParserOptions object and the option
   name.
 
+'ParserOutputPostCacheTransform': Called from ParserOutput::getText() to do
+post-cache transforms.
+$parserOutput: The ParserOutput object.
+&$text: The text being transformed, before core transformations are done.
+&$options: The options array being used for the transformation.
+
 'ParserSectionCreate': Called each time the parser creates a document section
 from wikitext. Use this to apply per-section modifications to HTML (like
 wrapping the section in a DIV).  Caveat: DIVs are valid wikitext, and a DIV
index 5e2845f..f99089b 100644 (file)
@@ -869,6 +869,7 @@ class ParserOptions {
 
        /**
         * Create "edit section" links?
+        * @deprecated since 1.31, use ParserOutput::getText() options instead.
         * @return bool
         */
        public function getEditSection() {
@@ -877,6 +878,7 @@ class ParserOptions {
 
        /**
         * Create "edit section" links?
+        * @deprecated since 1.31, use ParserOutput::getText() options instead.
         * @param bool|null $x New value (null is no change)
         * @return bool Old value
         */
index 3480a51..59c27e5 100644 (file)
@@ -144,6 +144,7 @@ class ParserOutput extends CacheTime {
        public $mSections = [];
 
        /**
+        * @deprecated since 1.31 Use getText() options.
         * @var bool $mEditSectionTokens prefix/suffix markers if edit sections were output as tokens.
         */
        public $mEditSectionTokens = false;
@@ -164,6 +165,7 @@ class ParserOutput extends CacheTime {
        public $mTimestamp;
 
        /**
+        * @deprecated since 1.31 Use getText() options.
         * @var bool $mTOCEnabled Whether TOC should be shown, can't override __NOTOC__.
         */
        public $mTOCEnabled = true;
@@ -250,9 +252,38 @@ class ParserOutput extends CacheTime {
                return $this->mText;
        }
 
-       public function getText() {
+       /**
+        * Get the output HTML
+        *
+        * @param array $options (since 1.31) Transformations to apply to the HTML
+        *  - allowTOC: (bool) Show the TOC, assuming there were enough headings
+        *     to generate one and `__NOTOC__` wasn't used. Default is true,
+        *     but might be statefully overridden.
+        *  - enableSectionEditLinks: (bool) Include section edit links, assuming
+        *    section edit link tokens are present in the HTML. Default is true,
+        *     but might be statefully overridden.
+        * @return string HTML
+        */
+       public function getText( $options = [] ) {
+               // @todo Warn if !array_key_exists( 'allowTOC', $options ) && empty( $this->mTOCEnabled )
+
+               // @todo Warn if !array_key_exists( 'enableSectionEditLinks', $options )
+               //     && !$this->mEditSectionTokens
+               //  Note that while $this->mEditSectionTokens defaults to false,
+               //  ParserOptions->getEditSection() defaults to true and Parser copies
+               //  that to us so true makes more sense as the stateless default.
+
+               $options += [
+                       // empty() here because old cached versions might lack the field somehow.
+                       // In that situation, the historical behavior (possibly buggy) is to remove the TOC.
+                       'allowTOC' => !empty( $this->mTOCEnabled ),
+                       'enableSectionEditLinks' => $this->mEditSectionTokens,
+               ];
                $text = $this->mText;
-               if ( $this->mEditSectionTokens ) {
+
+               Hooks::runWithoutAbort( 'ParserOutputPostCacheTransform', [ $this, &$text, &$options ] );
+
+               if ( $options['enableSectionEditLinks'] ) {
                        $text = preg_replace_callback(
                                self::EDITSECTION_REGEX,
                                function ( $m ) {
@@ -278,8 +309,7 @@ class ParserOutput extends CacheTime {
                        $text = preg_replace( self::EDITSECTION_REGEX, '', $text );
                }
 
-               // If you have an old cached version of this class - sorry, you can't disable the TOC
-               if ( isset( $this->mTOCEnabled ) && $this->mTOCEnabled ) {
+               if ( $options['allowTOC'] ) {
                        $text = str_replace( [ Parser::TOC_START, Parser::TOC_END ], '', $text );
                } else {
                        $text = preg_replace(
@@ -288,6 +318,7 @@ class ParserOutput extends CacheTime {
                                $text
                        );
                }
+
                return $text;
        }
 
@@ -339,6 +370,9 @@ class ParserOutput extends CacheTime {
                return $this->mSections;
        }
 
+       /**
+        * @deprecated since 1.31 Use getText() options.
+        */
        public function getEditSectionTokens() {
                return $this->mEditSectionTokens;
        }
@@ -426,6 +460,9 @@ class ParserOutput extends CacheTime {
                return $this->mLimitReportJSData;
        }
 
+       /**
+        * @deprecated since 1.31 Use getText() options.
+        */
        public function getTOCEnabled() {
                return $this->mTOCEnabled;
        }
@@ -454,6 +491,9 @@ class ParserOutput extends CacheTime {
                return wfSetVar( $this->mSections, $toc );
        }
 
+       /**
+        * @deprecated since 1.31 Use getText() options.
+        */
        public function setEditSectionTokens( $t ) {
                return wfSetVar( $this->mEditSectionTokens, $t );
        }
@@ -470,6 +510,9 @@ class ParserOutput extends CacheTime {
                return wfSetVar( $this->mTimestamp, $timestamp );
        }
 
+       /**
+        * @deprecated since 1.31 Use getText() options.
+        */
        public function setTOCEnabled( $flag ) {
                return wfSetVar( $this->mTOCEnabled, $flag );
        }
index ec8f0d0..441d60d 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use Wikimedia\TestingAccessWrapper;
+
 /**
  * @group Database
  *        ^--- trigger DB shadowing because we are using Title magic
@@ -89,4 +91,231 @@ class ParserOutputTest extends MediaWikiTestCase {
                $this->assertArrayNotHasKey( 'foo', $properties );
        }
 
+       /**
+        * @covers ParserOutput::getText
+        * @dataProvider provideGetText
+        * @param array $options Options to getText()
+        * @param array $poState ParserOptions state fields to set
+        * @param string $text Parser text
+        * @param string $expect Expected output
+        */
+       public function testGetText( $options, $poState, $text, $expect ) {
+               $this->setMwGlobals( [
+                       'wgArticlePath' => '/wiki/$1',
+                       'wgScriptPath' => '/w',
+                       'wgScript' => '/w/index.php',
+               ] );
+
+               $po = new ParserOutput( $text );
+
+               // Emulate Parser
+               $po->setEditSectionTokens( true );
+
+               if ( $poState ) {
+                       $wrap = TestingAccessWrapper::newFromObject( $po );
+                       foreach ( $poState as $key => $value ) {
+                               $wrap->$key = $value;
+                       }
+               }
+
+               $actual = $po->getText( $options );
+               $this->assertSame( $expect, $actual );
+       }
+
+       public static function provideGetText() {
+               // @codingStandardsIgnoreStart Generic.Files.LineLength
+               $text = <<<EOF
+<p>Test document.
+</p>
+<mw:toc><div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
+<li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
+<ul>
+<li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
+</ul>
+</li>
+<li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
+</ul>
+</div>
+</mw:toc>
+<h2><span class="mw-headline" id="Section_1">Section 1</span><mw:editsection page="Test Page" section="1">Section 1</mw:editsection></h2>
+<p>One
+</p>
+<h2><span class="mw-headline" id="Section_2">Section 2</span><mw:editsection page="Test Page" section="2">Section 2</mw:editsection></h2>
+<p>Two
+</p>
+<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span><mw:editsection page="Test Page" section="3">Section 2.1</mw:editsection></h3>
+<p>Two point one
+</p>
+<h2><span class="mw-headline" id="Section_3">Section 3</span><mw:editsection page="Test Page" section="4">Section 3</mw:editsection></h2>
+<p>Three
+</p>
+EOF;
+
+               return [
+                       'No stateless options, default state' => [
+                               [], [], $text, <<<EOF
+<p>Test document.
+</p>
+<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
+<li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
+<ul>
+<li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
+</ul>
+</li>
+<li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
+</ul>
+</div>
+
+<h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>One
+</p>
+<h2><span class="mw-headline" id="Section_2">Section 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=2" title="Edit section: Section 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>Two
+</p>
+<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=3" title="Edit section: Section 2.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
+<p>Two point one
+</p>
+<h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>Three
+</p>
+EOF
+                       ],
+                       'No stateless options, TOC statefully disabled' => [
+                               [], [ 'mTOCEnabled' => false ], $text, <<<EOF
+<p>Test document.
+</p>
+
+<h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>One
+</p>
+<h2><span class="mw-headline" id="Section_2">Section 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=2" title="Edit section: Section 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>Two
+</p>
+<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=3" title="Edit section: Section 2.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
+<p>Two point one
+</p>
+<h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>Three
+</p>
+EOF
+                       ],
+                       'No stateless options, section edits statefully disabled' => [
+                               [], [ 'mEditSectionTokens' => false ], $text, <<<EOF
+<p>Test document.
+</p>
+<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
+<li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
+<ul>
+<li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
+</ul>
+</li>
+<li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
+</ul>
+</div>
+
+<h2><span class="mw-headline" id="Section_1">Section 1</span></h2>
+<p>One
+</p>
+<h2><span class="mw-headline" id="Section_2">Section 2</span></h2>
+<p>Two
+</p>
+<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span></h3>
+<p>Two point one
+</p>
+<h2><span class="mw-headline" id="Section_3">Section 3</span></h2>
+<p>Three
+</p>
+EOF
+                       ],
+                       'Stateless options override stateful settings' => [
+                               [ 'allowTOC' => true, 'enableSectionEditLinks' => true ],
+                               [ 'mTOCEnabled' => false, 'mEditSectionTokens' => false ],
+                               $text, <<<EOF
+<p>Test document.
+</p>
+<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
+<li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
+<ul>
+<li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
+</ul>
+</li>
+<li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
+</ul>
+</div>
+
+<h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>One
+</p>
+<h2><span class="mw-headline" id="Section_2">Section 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=2" title="Edit section: Section 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>Two
+</p>
+<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=3" title="Edit section: Section 2.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
+<p>Two point one
+</p>
+<h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>Three
+</p>
+EOF
+                       ],
+                       'Statelessly disable section edit links' => [
+                               [ 'enableSectionEditLinks' => false ], [], $text, <<<EOF
+<p>Test document.
+</p>
+<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
+<li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
+<ul>
+<li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
+</ul>
+</li>
+<li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
+</ul>
+</div>
+
+<h2><span class="mw-headline" id="Section_1">Section 1</span></h2>
+<p>One
+</p>
+<h2><span class="mw-headline" id="Section_2">Section 2</span></h2>
+<p>Two
+</p>
+<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span></h3>
+<p>Two point one
+</p>
+<h2><span class="mw-headline" id="Section_3">Section 3</span></h2>
+<p>Three
+</p>
+EOF
+                       ],
+                       'Statelessly disable TOC' => [
+                               [ 'allowTOC' => false ], [], $text, <<<EOF
+<p>Test document.
+</p>
+
+<h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>One
+</p>
+<h2><span class="mw-headline" id="Section_2">Section 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=2" title="Edit section: Section 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>Two
+</p>
+<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=3" title="Edit section: Section 2.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
+<p>Two point one
+</p>
+<h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>Three
+</p>
+EOF
+                       ],
+               ];
+               // @codingStandardsIgnoreEnd
+       }
+
 }