Merge "resources: Make jquery.cookie a multi-file resource with changelog, licence"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 24 Sep 2018 17:38:15 +0000 (17:38 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 24 Sep 2018 17:38:15 +0000 (17:38 +0000)
includes/actions/McrUndoAction.php
includes/changetags/ChangeTags.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderModule.php
languages/i18n/en.json
languages/i18n/qqq.json
tests/phpunit/includes/OutputPageTest.php
tests/phpunit/includes/changetags/ChangeTagsTest.php

index 90d1f68..15a394d 100644 (file)
@@ -219,7 +219,7 @@ class McrUndoAction extends FormAction {
                return $newRev;
        }
 
-       private function generateDiff() {
+       private function generateDiffOrPreview() {
                $newRev = $this->getNewRevision();
                if ( $newRev->hasSameContent( $this->curRev ) ) {
                        throw new ErrorPageError( 'mcrundofailed', 'undo-nochange' );
@@ -232,7 +232,7 @@ class McrUndoAction extends FormAction {
                $newtitle = $this->context->msg( 'yourtext' )->parse();
 
                if ( $this->getRequest()->getCheck( 'wpPreview' ) ) {
-                       $diffEngine->renderNewRevision();
+                       $this->showPreview( $newRev );
                        return '';
                } else {
                        $diffText = $diffEngine->getDiff( $oldtitle, $newtitle );
@@ -241,6 +241,56 @@ class McrUndoAction extends FormAction {
                }
        }
 
+       private function showPreview( RevisionRecord $rev ) {
+               // Mostly copied from EditPage::getPreviewText()
+               $out = $this->getOutput();
+
+               try {
+                       $previewHTML = '';
+
+                       # provide a anchor link to the form
+                       $continueEditing = '<span class="mw-continue-editing">' .
+                               '[[#mw-mcrundo-form|' .
+                               $this->context->getLanguage()->getArrow() . ' ' .
+                               $this->context->msg( 'continue-editing' )->text() . ']]</span>';
+
+                       $note = $this->context->msg( 'previewnote' )->plain() . ' ' . $continueEditing;
+
+                       $parserOptions = $this->page->makeParserOptions( $this->context );
+                       $parserOptions->setIsPreview( true );
+                       $parserOptions->setIsSectionPreview( false );
+                       $parserOptions->enableLimitReport();
+
+                       $parserOutput = MediaWikiServices::getInstance()->getRevisionRenderer()
+                               ->getRenderedRevision( $rev, $parserOptions, $this->context->getUser() )
+                               ->getRevisionParserOutput();
+                       $previewHTML = $parserOutput->getText( [ 'enableSectionEditLinks' => false ] );
+
+                       $out->addParserOutputMetadata( $parserOutput );
+                       if ( count( $parserOutput->getWarnings() ) ) {
+                               $note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() );
+                       }
+               } catch ( MWContentSerializationException $ex ) {
+                       $m = $this->context->msg(
+                               'content-failed-to-parse',
+                               $ex->getMessage()
+                       );
+                       $note .= "\n\n" . $m->parse();
+                       $previewHTML = '';
+               }
+
+               $previewhead = "<div class='previewnote'>\n" .
+                       '<h2 id="mw-previewheader">' . $this->context->msg( 'preview' )->escaped() . "</h2>" .
+                       $out->parse( $note, true, /* interface */true ) . "<hr /></div>\n";
+
+               $pageViewLang = $this->getTitle()->getPageViewLanguage();
+               $attribs = [ 'lang' => $pageViewLang->getHtmlCode(), 'dir' => $pageViewLang->getDir(),
+                       'class' => 'mw-content-' . $pageViewLang->getDir() ];
+               $previewHTML = Html::rawElement( 'div', $attribs, $previewHTML );
+
+               $out->addHtml( $previewhead . $previewHTML );
+       }
+
        public function onSubmit( $data ) {
                global $wgUseRCPatrol;
 
@@ -306,7 +356,7 @@ class McrUndoAction extends FormAction {
                                'vertical-label' => true,
                                'raw' => true,
                                'default' => function () {
-                                       return $this->generateDiff();
+                                       return $this->generateDiffOrPreview();
                                }
                        ],
                        'summary' => [
@@ -343,6 +393,7 @@ class McrUndoAction extends FormAction {
 
                $labelAsPublish = $this->context->getConfig()->get( 'EditSubmitButtonLabelPublish' );
 
+               $form->setId( 'mw-mcrundo-form' );
                $form->setSubmitName( 'wpSave' );
                $form->setSubmitTooltip( $labelAsPublish ? 'publish' : 'save' );
                $form->setSubmitTextMsg( $labelAsPublish ? 'publishchanges' : 'savechanges' );
index 45a35c0..8dc63e5 100644 (file)
@@ -928,13 +928,14 @@ class ChangeTags {
                        );
                }
 
-               $dbw->replace(
-                       'valid_tag',
-                       [ 'vt_tag' ],
-                       [ 'vt_tag' => $tag ],
-                       __METHOD__
-               );
-
+               if ( $wgChangeTagsSchemaMigrationStage < MIGRATION_NEW ) {
+                       $dbw->replace(
+                               'valid_tag',
+                               [ 'vt_tag' ],
+                               [ 'vt_tag' => $tag ],
+                               __METHOD__
+                       );
+               }
                // clear the memcache of defined tags
                self::purgeTagCacheAll();
        }
@@ -967,7 +968,9 @@ class ChangeTags {
                        );
                }
 
-               $dbw->delete( 'valid_tag', [ 'vt_tag' => $tag ], __METHOD__ );
+               if ( $wgChangeTagsSchemaMigrationStage < MIGRATION_NEW ) {
+                       $dbw->delete( 'valid_tag', [ 'vt_tag' => $tag ], __METHOD__ );
+               }
 
                // clear the memcache of defined tags
                self::purgeTagCacheAll();
@@ -1458,7 +1461,7 @@ class ChangeTags {
        /**
         * Lists tags explicitly defined in the `valid_tag` table of the database.
         * Tags in table 'change_tag' which are not in table 'valid_tag' are not
-        * included.
+        * included. In case of new backend loads the data from `change_tag_def` table.
         *
         * Tries memcached first.
         *
@@ -1473,11 +1476,16 @@ class ChangeTags {
                        $cache->makeKey( 'valid-tags-db' ),
                        WANObjectCache::TTL_MINUTE * 5,
                        function ( $oldValue, &$ttl, array &$setOpts ) use ( $fname ) {
+                               global $wgChangeTagsSchemaMigrationStage;
                                $dbr = wfGetDB( DB_REPLICA );
 
                                $setOpts += Database::getCacheSetOptions( $dbr );
 
-                               $tags = $dbr->selectFieldValues( 'valid_tag', 'vt_tag', [], $fname );
+                               if ( $wgChangeTagsSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                                       $tags = self::listExplicitlyDefinedTagsNewBackend();
+                               } else {
+                                       $tags = $dbr->selectFieldValues( 'valid_tag', 'vt_tag', [], $fname );
+                               }
 
                                return array_filter( array_unique( $tags ) );
                        },
@@ -1489,6 +1497,22 @@ class ChangeTags {
                );
        }
 
+       /**
+        * Lists tags explicitly user defined tags. When ctd_user_defined is true.
+        *
+        * @return string[] Array of strings: tags
+        * @since 1.25
+        */
+       private static function listExplicitlyDefinedTagsNewBackend() {
+               $dbr = wfGetDB( DB_REPLICA );
+               return $dbr->selectFieldValues(
+                       'change_tag_def',
+                       'ctd_name',
+                       [ 'ctd_user_defined' => 1 ],
+                       __METHOD__
+               );
+       }
+
        /**
         * Lists tags defined by core or extensions using the ListDefinedTags hook.
         * Extensions need only define those tags they deem to be in active use.
index 604a140..5621dcd 100644 (file)
@@ -37,7 +37,7 @@ use Wikimedia\WrappedString;
  */
 class ResourceLoader implements LoggerAwareInterface {
        /** @var int */
-       protected static $filterCacheVersion = 8;
+       const CACHE_VERSION = 8;
 
        /** @var bool */
        protected static $debugMode = null;
@@ -199,7 +199,8 @@ class ResourceLoader implements LoggerAwareInterface {
                        'resourceloader',
                        'filter',
                        $filter,
-                       self::$filterCacheVersion, md5( $data )
+                       self::CACHE_VERSION,
+                       md5( $data )
                );
 
                $result = $cache->get( $key );
index a507ad3..30b2aa7 100644 (file)
@@ -813,7 +813,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                        } else {
                                // Infer changes based on definition and other metrics
                                $summary = $this->getDefinitionSummary( $context );
-                               if ( !isset( $summary['_cacheEpoch'] ) ) {
+                               if ( !isset( $summary['_class'] ) ) {
                                        throw new LogicException( 'getDefinitionSummary must call parent method' );
                                }
                                $str = json_encode( $summary );
@@ -883,7 +883,9 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
        public function getDefinitionSummary( ResourceLoaderContext $context ) {
                return [
                        '_class' => static::class,
-                       '_cacheEpoch' => $this->getConfig()->get( 'CacheEpoch' ),
+                       // Make sure that when filter cache for minification is invalidated,
+                       // we also change the HTTP urls and mw.loader.store keys (T176884).
+                       '_cacheVersion' => ResourceLoader::CACHE_VERSION,
                ];
        }
 
index 4d28bd6..1831b9f 100644 (file)
        "mcrundofailed": "Undo failed",
        "mcrundo-missingparam": "Missing required parameters on request.",
        "mcrundo-changed": "The page has been changed since you viewed the diff. Please review the new change.",
+       "mcrundo-parse-failed": "Failed to parse the new revision: $1",
        "semicolon-separator": ";&#32;",
        "comma-separator": ",&#32;",
        "colon-separator": ":&#32;",
index cf4446d..a65c3c8 100644 (file)
        "mcrundofailed": "Title of the error page when an editless undo fails.",
        "mcrundo-missingparam": "Error displayed when parameters for action=mcrundo are missing",
        "mcrundo-changed": "Message displayed when the page has been edited between the loading and submission of an editless undo.",
+       "mcrundo-parse-failed": "Error message indicating that the page's content can not be parsed because it is syntactically invalid. This may occurr for content types using serialization or a strict markup syntax.\n\nParameters:\n* $1 – specific error message",
        "semicolon-separator": "{{optional}}",
        "comma-separator": "{{optional}}\n\nWarning: languages have different usages of punctuation, and sometimes they are swapped (e.g. openining and closing quotation marks, or full stop and colon in Armenian), or change their form (the full stop in Chinese and Japanese, the prefered \"colon\" in Armenian used in fact as the regular full stop, the comma in Arabic, Armenian, and Chinese...)\n\nTheir spacing (before or after) may also vary across languages (for example French requires a non-breaking space, preferably narrow if the browser supports NNBSP, on the inner side of some punctuations like quotation/question/exclamation marks, colon, and semicolons).",
        "colon-separator": "{{optional}}\nChange it only if your language uses another character for ':' or it needs an extra space before the colon.",
index 42ea9ed..d334b73 100644 (file)
@@ -1800,7 +1800,7 @@ class OutputPageTest extends MediaWikiTestCase {
                                'exemptStyleModules' => [ 'site' => [ 'site.styles' ], 'user' => [ 'user.styles' ] ],
                                '<meta name="ResourceLoaderDynamicStyles" content=""/>' . "\n" .
                                '<link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=en&amp;modules=site.styles&amp;only=styles&amp;skin=fallback"/>' . "\n" .
-                               '<link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=en&amp;modules=user.styles&amp;only=styles&amp;skin=fallback&amp;version=1e9z0ox"/>',
+                               '<link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=en&amp;modules=user.styles&amp;only=styles&amp;skin=fallback&amp;version=1ai9g6t"/>',
                        ],
                        'custom modules' => [
                                'exemptStyleModules' => [
@@ -1811,7 +1811,7 @@ class OutputPageTest extends MediaWikiTestCase {
                                '<link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=en&amp;modules=example.site.a%2Cb&amp;only=styles&amp;skin=fallback"/>' . "\n" .
                                '<link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=en&amp;modules=site.styles&amp;only=styles&amp;skin=fallback"/>' . "\n" .
                                '<link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=en&amp;modules=example.user&amp;only=styles&amp;skin=fallback&amp;version=0a56zyi"/>' . "\n" .
-                               '<link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=en&amp;modules=user.styles&amp;only=styles&amp;skin=fallback&amp;version=1e9z0ox"/>',
+                               '<link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=en&amp;modules=user.styles&amp;only=styles&amp;skin=fallback&amp;version=1ai9g6t"/>',
                        ],
                ];
                // phpcs:enable
index c770029..64c3224 100644 (file)
@@ -14,6 +14,7 @@ class ChangeTagsTest extends MediaWikiTestCase {
                $this->tablesUsed[] = 'change_tag';
                $this->tablesUsed[] = 'change_tag_def';
                $this->tablesUsed[] = 'tag_summary';
+               $this->tablesUsed[] = 'valid_tag';
        }
 
        // TODO only modifyDisplayQuery and getSoftwareTags are tested, nothing else is
@@ -592,4 +593,83 @@ class ChangeTagsTest extends MediaWikiTestCase {
 
                $this->assertEquals( [ 'tag1' => 2, 'tag2' => 1 ], ChangeTags::tagUsageStatistics() );
        }
+
+       public function testListExplicitlyDefinedTagsOld() {
+               $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_OLD );
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->delete( 'change_tag', '*' );
+               $dbw->delete( 'change_tag_def', '*' );
+               $dbw->delete( 'valid_tag', '*' );
+
+               $rcId = 123;
+               ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
+               ChangeTags::defineTag( 'tag2' );
+
+               $this->assertEquals( [ 'tag2' ], ChangeTags::listExplicitlyDefinedTags() );
+               $dbr = wfGetDB( DB_REPLICA );
+               $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_user_defined' ], '' );
+               $this->assertEquals( [], iterator_to_array( $res, false ) );
+
+               $this->assertEquals( [ 'tag2' ], $dbr->selectFieldValues( 'valid_tag', 'vt_tag', '' ) );
+       }
+
+       public function testListExplicitlyDefinedTagsWriteBoth() {
+               $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->delete( 'change_tag', '*' );
+               $dbw->delete( 'change_tag_def', '*' );
+               $dbw->delete( 'valid_tag', '*' );
+
+               $rcId = 123;
+               ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
+               ChangeTags::defineTag( 'tag2' );
+
+               $this->assertEquals( [ 'tag2' ], ChangeTags::listExplicitlyDefinedTags() );
+               $dbr = wfGetDB( DB_REPLICA );
+
+               $expected = [
+                       (object)[
+                               'ctd_name' => 'tag1',
+                               'ctd_user_defined' => 0
+                       ],
+                       (object)[
+                               'ctd_name' => 'tag2',
+                               'ctd_user_defined' => 1
+                       ],
+               ];
+               $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_user_defined' ], '' );
+               $this->assertEquals( $expected, iterator_to_array( $res, false ) );
+
+               $this->assertEquals( [ 'tag2' ], $dbr->selectFieldValues( 'valid_tag', 'vt_tag', '' ) );
+       }
+
+       public function testListExplicitlyDefinedTagsNew() {
+               $this->setMwGlobals( 'wgChangeTagsSchemaMigrationStage', MIGRATION_NEW );
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->delete( 'change_tag', '*' );
+               $dbw->delete( 'change_tag_def', '*' );
+               $dbw->delete( 'valid_tag', '*' );
+
+               $rcId = 123;
+               ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
+               ChangeTags::defineTag( 'tag2' );
+
+               $this->assertEquals( [ 'tag2' ], ChangeTags::listExplicitlyDefinedTags() );
+               $dbr = wfGetDB( DB_REPLICA );
+
+               $expected = [
+                       (object)[
+                               'ctd_name' => 'tag1',
+                               'ctd_user_defined' => 0
+                       ],
+                       (object)[
+                               'ctd_name' => 'tag2',
+                               'ctd_user_defined' => 1
+                       ],
+               ];
+               $res = $dbr->select( 'change_tag_def', [ 'ctd_name', 'ctd_user_defined' ], '' );
+               $this->assertEquals( $expected, iterator_to_array( $res, false ) );
+
+               $this->assertEquals( [], $dbr->selectFieldValues( 'valid_tag', 'vt_tag', '' ) );
+       }
 }