Merge "Move call of Sanitizer::decodeCharReferences from Skin to Parser"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 13 Sep 2017 17:54:13 +0000 (17:54 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 13 Sep 2017 17:54:13 +0000 (17:54 +0000)
16 files changed:
includes/CommentStore.php
includes/CommentStoreComment.php
includes/Linker.php
includes/Sanitizer.php
includes/changes/EnhancedChangesList.php
includes/diff/DifferenceEngine.php
includes/filerepo/file/LocalFile.php
includes/page/WikiPage.php
includes/parser/Parser.php
includes/skins/Skin.php
resources/Resources.php
resources/src/jquery/jquery.byteLength.js
resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
tests/parser/parserTests.txt
tests/phpunit/includes/SanitizerTest.php

index 2ed21d1..b8a31e6 100644 (file)
@@ -367,26 +367,7 @@ class CommentStore {
         * @return CommentStoreComment
         */
        public function createComment( IDatabase $dbw, $comment, array $data = null ) {
-               global $wgContLang;
-
-               if ( !$comment instanceof CommentStoreComment ) {
-                       if ( $data !== null ) {
-                               foreach ( $data as $k => $v ) {
-                                       if ( substr( $k, 0, 1 ) === '_' ) {
-                                               throw new InvalidArgumentException( 'Keys in $data beginning with "_" are reserved' );
-                                       }
-                               }
-                       }
-                       if ( $comment instanceof Message ) {
-                               $message = clone $comment;
-                               $text = $message->inLanguage( $wgContLang ) // Avoid $wgForceUIMsgAsContentMsg
-                                       ->setInterfaceMessageFlag( true )
-                                       ->text();
-                               $comment = new CommentStoreComment( null, $text, $message, $data );
-                       } else {
-                               $comment = new CommentStoreComment( null, $comment, null, $data );
-                       }
-               }
+               $comment = CommentStoreComment::newUnsavedComment( $comment, $data );
 
                # Truncate comment in a Unicode-sensitive manner
                $comment->text = $this->lang->truncate( $comment->text, self::MAX_COMMENT_LENGTH );
index afc1374..3920ba0 100644 (file)
@@ -42,7 +42,7 @@ class CommentStoreComment {
        public $data;
 
        /**
-        * @private For use by CommentStore only
+        * @private For use by CommentStore only. Use self::newUnsavedComment() instead.
         * @param int|null $id
         * @param string $text
         * @param Message|null $message
@@ -54,4 +54,39 @@ class CommentStoreComment {
                $this->message = $message ?: new RawMessage( '$1', [ $text ] );
                $this->data = $data;
        }
+
+       /**
+        * Create a new, unsaved CommentStoreComment
+        *
+        * @param string|Message|CommentStoreComment $comment Comment text or Message object.
+        *  A CommentStoreComment is also accepted here, in which case it is returned unchanged.
+        * @param array|null $data Structured data to store. Keys beginning with '_' are reserved.
+        *  Ignored if $comment is a CommentStoreComment.
+        * @return CommentStoreComment
+        */
+       public static function newUnsavedComment( $comment, array $data = null ) {
+               global $wgContLang;
+
+               if ( $comment instanceof CommentStoreComment ) {
+                       return $comment;
+               }
+
+               if ( $data !== null ) {
+                       foreach ( $data as $k => $v ) {
+                               if ( substr( $k, 0, 1 ) === '_' ) {
+                                       throw new InvalidArgumentException( 'Keys in $data beginning with "_" are reserved' );
+                               }
+                       }
+               }
+
+               if ( $comment instanceof Message ) {
+                       $message = clone $comment;
+                       $text = $message->inLanguage( $wgContLang ) // Avoid $wgForceUIMsgAsContentMsg
+                               ->setInterfaceMessageFlag( true )
+                               ->text();
+                       return new CommentStoreComment( null, $text, $message, $data );
+               } else {
+                       return new CommentStoreComment( null, $comment, null, $data );
+               }
+       }
 }
index c24ae41..403b10a 100644 (file)
@@ -1175,7 +1175,7 @@ class Linker {
                                                        $sectionTitle = Title::newFromText( '#' . $section );
                                                } else {
                                                        $sectionTitle = Title::makeTitleSafe( $title->getNamespace(),
-                                                               $title->getDBkey(), $section );
+                                                               $title->getDBkey(), Sanitizer::decodeCharReferences( $section ) );
                                                }
                                                if ( $sectionTitle ) {
                                                        $link = Linker::makeCommentLink( $sectionTitle, $wgLang->getArrow(), $wikiId, 'noclasses' );
index 7d17cd1..a7f963a 100644 (file)
@@ -1203,8 +1203,6 @@ class Sanitizer {
                global $wgExperimentalHtmlIds;
                $options = (array)$options;
 
-               $id = self::decodeCharReferences( $id );
-
                if ( $wgExperimentalHtmlIds && !in_array( 'legacy', $options ) ) {
                        $id = preg_replace( '/[ \t\n\r\f_\'"&#%]+/', '_', $id );
                        $id = trim( $id, '_' );
@@ -1313,8 +1311,6 @@ class Sanitizer {
         * @return string
         */
        private static function escapeIdInternal( $id, $mode ) {
-               $id = self::decodeCharReferences( $id );
-
                switch ( $mode ) {
                        case 'html5':
                                $id = str_replace( ' ', '_', $id );
index 8e24efe..0df6828 100644 (file)
@@ -383,7 +383,7 @@ class EnhancedChangesList extends ChangesList {
                ) {
                        $classes[] = 'mw-enhanced-watched';
                }
-               $classes = array_merge( $classes, $this->getHTMLClassesForFilters( $rcObj ) );
+               $classes = array_merge( $classes, $this->getHTMLClasses( $rcObj, $rcObj->watched ) );
 
                $separator = ' <span class="mw-changeslist-separator">. .</span> ';
 
index 34f2852..ed8cbb4 100644 (file)
@@ -181,7 +181,8 @@ class DifferenceEngine extends ContextSource {
        public function deletedLink( $id ) {
                if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) {
                        $dbr = wfGetDB( DB_REPLICA );
-                       $row = $dbr->selectRow( 'archive', '*',
+                       $row = $dbr->selectRow( 'archive',
+                               Revision::selectArchiveFields(),
                                [ 'ar_rev_id' => $id ],
                                __METHOD__ );
                        if ( $row ) {
index 8aea7ab..96e7a7e 100644 (file)
@@ -1129,11 +1129,9 @@ class LocalFile extends File {
 
                if ( $this->historyLine == 0 ) { // called for the first time, return line from cur
                        $this->historyRes = $dbr->select( 'image',
-                               [
-                                       '*',
-                                       "'' AS oi_archive_name",
-                                       '0 as oi_deleted',
-                                       'img_sha1'
+                               self::selectFields() + [
+                                       'oi_archive_name' => $dbr->addQuotes( '' ),
+                                       'oi_deleted' => 0,
                                ],
                                [ 'img_name' => $this->title->getDBkey() ],
                                $fname
@@ -1145,7 +1143,9 @@ class LocalFile extends File {
                                return false;
                        }
                } elseif ( $this->historyLine == 1 ) {
-                       $this->historyRes = $dbr->select( 'oldimage', '*',
+                       $this->historyRes = $dbr->select(
+                               'oldimage',
+                               OldLocalFile::selectFields(),
                                [ 'oi_name' => $this->title->getDBkey() ],
                                $fname,
                                [ 'ORDER BY' => 'oi_timestamp DESC' ]
index 6fc36f6..bbf22ba 100644 (file)
@@ -1921,10 +1921,10 @@ class WikiPage implements Page, IDBAccessObject {
                                        $wikiPage = $this;
                                        // Trigger post-create hook
                                        $params = [ &$wikiPage, &$user, $content, $summary,
-                                               $flags & EDIT_MINOR, null, null, &$flags, $revision ];
+                                                               $flags & EDIT_MINOR, null, null, &$flags, $revision ];
                                        Hooks::run( 'PageContentInsertComplete', $params );
                                        // Trigger post-save hook
-                                       $params = array_merge( $params, [ &$status, $meta['baseRevId'] ] );
+                                       $params = array_merge( $params, [ &$status, $meta['baseRevId'], 0 ] );
                                        Hooks::run( 'PageContentSaveComplete', $params );
                                }
                        ),
index ff4936d..e901f6f 100644 (file)
@@ -4204,6 +4204,8 @@ class Parser {
                        # Save headline for section edit hint before it's escaped
                        $headlineHint = $safeHeadline;
 
+                       # Decode HTML entities
+                       $safeHeadline = Sanitizer::decodeCharReferences( $safeHeadline );
                        $fallbackHeadline = Sanitizer::escapeIdForAttribute( $safeHeadline, Sanitizer::ID_FALLBACK );
                        $linkAnchor = Sanitizer::escapeIdForLink( $safeHeadline );
                        $safeHeadline = Sanitizer::escapeIdForAttribute( $safeHeadline, Sanitizer::ID_PRIMARY );
@@ -5764,6 +5766,7 @@ class Parser {
                # Strip out wikitext links(they break the anchor)
                $text = $this->stripSectionName( $text );
                $text = Sanitizer::normalizeSectionNameWhitespace( $text );
+               $text = Sanitizer::decodeCharReferences( $text );
                return '#' . Sanitizer::escapeIdForLink( $text );
        }
 
@@ -5782,6 +5785,7 @@ class Parser {
                # Strip out wikitext links(they break the anchor)
                $text = $this->stripSectionName( $text );
                $text = Sanitizer::normalizeSectionNameWhitespace( $text );
+               $text = Sanitizer::decodeCharReferences( $text );
 
                if ( isset( $wgFragmentMode[1] ) && $wgFragmentMode[1] === 'legacy' ) {
                        // ForAttribute() and ForLink() are the same for legacy encoding
index f9090e1..54bba30 100644 (file)
@@ -1609,7 +1609,7 @@ abstract class Skin extends ContextSource {
 
                $result .= implode(
                        '<span class="mw-editsection-divider">'
-                               . wfMessage( 'pipe-separator' )->inLanguage( $lang )->text()
+                               . wfMessage( 'pipe-separator' )->inLanguage( $lang )->escaped()
                                . '</span>',
                        $linksHtml
                );
index f0aa9ec..10786da 100644 (file)
@@ -1091,6 +1091,9 @@ return [
                'scripts' => [
                        'resources/src/mediawiki/htmlform/htmlform.Checker.js',
                ],
+               'dependencies' => [
+                       'jquery.throttle-debounce',
+               ],
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.htmlform.ooui' => [
@@ -1772,6 +1775,7 @@ return [
                        'resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js',
                ],
                'dependencies' => [
+                       'jquery.byteLength',
                        'oojs',
                        'mediawiki.api',
                        'mediawiki.api.options',
index 7fe25ee..222f14a 100644 (file)
@@ -11,7 +11,7 @@
  * @static
  * @inheritable
  * @param {string} str
- * @return {string}
+ * @return {number}
  */
 jQuery.byteLength = function ( str ) {
        // This basically figures out how many bytes a UTF-16 string (which is what js sees)
index f37229f..ee74ac5 100644 (file)
                // Stringify state
                stringified = JSON.stringify( state );
 
-               if ( stringified.length > 65535 ) {
+               if ( $.byteLength( stringified ) > 65535 ) {
                        // Sanity check, since the preference can only hold that.
                        return;
                }
index 67edb4f..83e5796 100644 (file)
                                );
                                filtersWidget.setTopSection( wlTopSection.$element );
                        } // end Special:WL
+
+                       /**
+                        * Fired when initialization of the filtering interface for changes list is complete.
+                        *
+                        * @event structuredChangeFilters_ui_initialized
+                        * @member mw.hook
+                        */
+                       mw.hook( 'structuredChangeFilters.ui.initialized' ).fire();
                }
        };
 
index ff777e7..d476373 100644 (file)
@@ -29305,7 +29305,7 @@ Decoding of HTML entities in headings and links for IDs and link fragments (T103
 [[#A&B&amp;C&amp;amp;D&amp;amp;amp;E]]
 !! html/php
 <h2><span class="mw-headline" id="A.26B.26C.26amp.3BD.26amp.3Bamp.3BE">A&amp;B&amp;C&amp;amp;D&amp;amp;amp;E</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: A&amp;B&amp;C&amp;amp;D&amp;amp;amp;E">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
-<p><a href="#A.26B.26C.26D.26amp.3BE">#A&amp;B&amp;C&amp;amp;D&amp;amp;amp;E</a>
+<p><a href="#A.26B.26C.26amp.3BD.26amp.3Bamp.3BE">#A&amp;B&amp;C&amp;amp;D&amp;amp;amp;E</a>
 </p>
 !! end
 
index 350cb70..7256694 100644 (file)
@@ -376,7 +376,7 @@ class SanitizerTest extends MediaWikiTestCase {
                        [ '\'', '.27' ],
                        [ '§', '.C2.A7' ],
                        [ 'Test:A & B/Here', 'Test:A_.26_B.2FHere' ],
-                       [ 'A&B&amp;C&amp;amp;D&amp;amp;amp;E', 'A.26B.26C.26amp.3BD.26amp.3Bamp.3BE' ],
+                       [ 'A&B&amp;C&amp;amp;D&amp;amp;amp;E', 'A.26B.26amp.3BC.26amp.3Bamp.3BD.26amp.3Bamp.3Bamp.3BE' ],
                ];
        }
 
@@ -452,10 +452,11 @@ class SanitizerTest extends MediaWikiTestCase {
 
        public function provideEscapeIdForStuff() {
                // Test inputs and outputs
-               $text = 'foo тест_#%!\'()[]:<>';
-               $legacyEncoded = 'foo_.D1.82.D0.B5.D1.81.D1.82_.23.25.21.27.28.29.5B.5D:.3C.3E';
-               $html5Encoded = 'foo_тест_#%!\'()[]:<>';
-               $html5Experimental = 'foo_тест_!_()[]:<>';
+               $text = 'foo тест_#%!\'()[]:<>&&amp;&amp;amp;';
+               $legacyEncoded = 'foo_.D1.82.D0.B5.D1.81.D1.82_.23.25.21.27.28.29.5B.5D:.3C.3E' .
+                       '.26.26amp.3B.26amp.3Bamp.3B';
+               $html5Encoded = 'foo_тест_#%!\'()[]:<>&&amp;&amp;amp;';
+               $html5Experimental = 'foo_тест_!_()[]:<>_amp;_amp;amp;';
 
                // Settings: last element is $wgExternalInterwikiFragmentMode, the rest is $wgFragmentMode
                $legacy = [ 'legacy', 'legacy' ];