Merge "Fix FakeTemplate usage in LoginSignupSpecialPage"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 15 Jun 2016 16:06:41 +0000 (16:06 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 15 Jun 2016 16:06:41 +0000 (16:06 +0000)
43 files changed:
Gemfile
Gemfile.lock
RELEASE-NOTES-1.28
docs/hooks.txt
includes/DefaultSettings.php
includes/EditPage.php
includes/PHPVersionCheck.php
includes/PHPVersionError.php [deleted file]
includes/Revision.php
includes/Setup.php
includes/api/ApiStashEdit.php
includes/api/i18n/en.json
includes/api/i18n/qqq.json
includes/changes/EnhancedChangesList.php
includes/deferred/LinksDeletionUpdate.php
includes/installer/Installer.php
includes/installer/i18n/en.json
includes/installer/i18n/qqq.json
includes/jobqueue/jobs/DeleteLinksJob.php
includes/media/FormatMetadata.php
includes/page/WikiPage.php
includes/parser/Parser.php
includes/specialpage/LoginSignupSpecialPage.php
includes/specialpage/SpecialPage.php
includes/specials/SpecialNewpages.php
includes/specials/SpecialRecentchanges.php
languages/Language.php
languages/i18n/en.json
languages/i18n/qqq.json
maintenance/Maintenance.php
maintenance/findHooks.php
maintenance/install.php
maintenance/update.php
resources/Resources.php
resources/src/mediawiki.action/mediawiki.action.edit.stash.js
resources/src/mediawiki.special/mediawiki.special.search.styles.css
resources/src/mediawiki/mediawiki.ForeignUpload.js
resources/src/startup.js
tests/parser/parserTest.inc
tests/parser/parserTests.txt
tests/phpunit/includes/parser/NewParserTest.php
tests/phpunit/mocks/media/MockOggHandler.php
tests/qunit/data/testrunner.js

diff --git a/Gemfile b/Gemfile
index fa3a025..19d2f52 100644 (file)
--- a/Gemfile
+++ b/Gemfile
@@ -1,5 +1,5 @@
 source 'https://rubygems.org'
 
-gem 'mediawiki_selenium', '~> 1.7'
+gem 'mediawiki_selenium', '~> 1.7', '>= 1.7.1'
 gem 'rake', '~> 11.1', '>= 11.1.1'
 gem 'rubocop', '~> 0.32.1', require: false
index 2bbabd1..2d6e655 100644 (file)
@@ -34,22 +34,22 @@ GEM
       domain_name (~> 0.5)
     i18n (0.7.0)
     json (1.8.3)
-    mediawiki_api (0.5.0)
+    mediawiki_api (0.6.0)
       faraday (~> 0.9, >= 0.9.0)
       faraday-cookie_jar (~> 0.0, >= 0.0.6)
-    mediawiki_selenium (1.7.0)
+    mediawiki_selenium (1.7.1)
       cucumber (~> 1.3, >= 1.3.20)
       headless (~> 2.0, >= 2.1.0)
       json (~> 1.8, >= 1.8.1)
-      mediawiki_api (~> 0.5, >= 0.5.0)
+      mediawiki_api (~> 0.6, >= 0.6.0)
       page-object (~> 1.0)
       rest-client (~> 1.6, >= 1.6.7)
       rspec-core (~> 2.14, >= 2.14.4)
       rspec-expectations (~> 2.14, >= 2.14.4)
       syntax (~> 1.2, >= 1.2.0)
       thor (~> 0.19, >= 0.19.1)
-    mime-types (2.99.1)
-    multi_json (1.11.3)
+    mime-types (2.99.2)
+    multi_json (1.12.1)
     multi_test (0.1.2)
     multipart-post (2.0.0)
     netrc (0.11.0)
@@ -79,11 +79,11 @@ GEM
       ruby-progressbar (~> 1.4)
     ruby-progressbar (1.7.5)
     rubyzip (1.2.0)
-    selenium-webdriver (2.53.0)
+    selenium-webdriver (2.53.1)
       childprocess (~> 0.5)
       rubyzip (~> 1.0)
       websocket (~> 1.0)
-    syntax (1.2.0)
+    syntax (1.2.1)
     thor (0.19.1)
     unf (0.1.4)
       unf_ext
@@ -97,7 +97,7 @@ PLATFORMS
   ruby
 
 DEPENDENCIES
-  mediawiki_selenium (~> 1.7)
+  mediawiki_selenium (~> 1.7, >= 1.7.1)
   rake (~> 11.1, >= 11.1.1)
   rubocop (~> 0.32.1)
 
index 7885f04..360f6d5 100644 (file)
@@ -13,6 +13,9 @@ production.
   is configurable via $wgSessionPbkdf2Iterations.
 * Upload dialog's file upload log comment can now be configured separately for
   local and foreign uploads.
+* $wgForeignUploadTargets now defaults to `[ 'local' ]`, where `'local'`
+  signifies local uploads. A value of `[]` (empty array) now means that
+  no upload targets are allowed, effectively disabling the upload dialog.
 
 === New features in 1.28 ===
 * User::isBot() method for checking if an account is a bot role account.
index e0b91f2..1d11893 100644 (file)
@@ -1350,6 +1350,7 @@ $changesList: EnhancedChangesList object
 &$data: An array with all the components that will be joined in order to create the line
 $block: An array of RecentChange objects in that block
 $rc: The RecentChange object for this line
+&$classes: An array of classes to change
 
 'EnhancedChangesListModifyBlockLineData': to alter data used to build
 a non-grouped recent change line in EnhancedChangesList.
@@ -2402,6 +2403,8 @@ that tests continue to run properly.
 $page: the WikiPage of the candidate edit
 $content: the Content object of the candidate edit
 $output: the ParserOutput result of the candidate edit
+$summary: the change summary of the candidate edit
+$user: the User considering the edit
 
 'PasswordPoliciesForUser': Alter the effective password policy for a user.
 $user: User object whose policy you are modifying
index 2607797..f176556 100644 (file)
@@ -529,11 +529,12 @@ $wgUseInstantCommons = false;
  * Array of foreign file repo names (set in $wgForeignFileRepos above) that
  * are allowable upload targets. These wikis must have some method of
  * authentication (i.e. CentralAuth), and be CORS-enabled for this wiki.
+ * The string 'local' signifies the default local file repository.
  *
  * Example:
  * $wgForeignUploadTargets = array( 'shared' );
  */
-$wgForeignUploadTargets = [];
+$wgForeignUploadTargets = [ 'local' ];
 
 /**
  * Configuration for file uploads using the embeddable upload dialog
index f2403fe..66ee161 100644 (file)
@@ -2690,6 +2690,18 @@ class EditPage {
                        }
                }
 
+               // Set a hidden field so JS knows what edit form mode we are in
+               if ( $this->isConflict ) {
+                       $mode = 'conflict';
+               } elseif ( $this->preview ) {
+                       $mode = 'preview';
+               } elseif ( $this->diff ) {
+                       $mode = 'diff';
+               } else {
+                       $mode = 'text';
+               }
+               $wgOut->addHTML( Html::hidden( 'mode', $mode, [ 'id' => 'mw-edit-mode' ] ) );
+
                // Marker for detecting truncated form data.  This must be the last
                // parameter sent in order to be of use, so do not move me.
                $wgOut->addHTML( Html::hidden( 'wpUltimateParam', true ) );
@@ -3603,7 +3615,7 @@ HTML
         */
        function getPreviewText() {
                global $wgOut, $wgUser, $wgRawHtml, $wgLang;
-               global $wgAllowUserCss, $wgAllowUserJs, $wgAjaxEditStash;
+               global $wgAllowUserCss, $wgAllowUserJs;
 
                $stats = $wgOut->getContext()->getStats();
 
@@ -3713,15 +3725,6 @@ HTML
                                $this->mTitle, $pstContent, $wgUser );
                        $parserOutput = $pstContent->getParserOutput( $this->mTitle, null, $parserOptions );
 
-                       # Try to stash the edit for the final submission step
-                       # @todo: different date format preferences cause cache misses
-                       if ( $wgAjaxEditStash ) {
-                               ApiStashEdit::stashEditFromPreview(
-                                       $this->getArticle(), $content, $pstContent,
-                                       $parserOutput, $parserOptions, $parserOptions, wfTimestampNow()
-                               );
-                       }
-
                        $parserOutput->setEditSectionTokens( false ); // no section edit links
                        $previewHTML = $parserOutput->getText();
                        $this->mParserOutput = $parserOutput;
index ab8aada..018c6f8 100644 (file)
@@ -45,6 +45,29 @@ function wfEntryPointCheck( $entryPoint ) {
                // @codingStandardsIgnoreEnd
                wfMissingVendorError( $entryPoint, $mwVersion );
        }
+
+       // List of functions and their associated PHP extension to check for
+       // @codingStandardsIgnoreStart Generic.Arrays.DisallowLongArraySyntax
+       $extensions = array(
+               'mb_substr'   => 'mbstring',
+               'utf8_encode' => 'xml',
+               'ctype_digit' => 'ctype',
+               'json_decode' => 'json',
+               'iconv'       => 'iconv',
+       );
+       // List of extensions we're missing
+       $missingExtensions = array();
+       // @codingStandardsIgnoreEnd
+
+       foreach ( $extensions as $function => $extension ) {
+               if ( !function_exists( $function ) ) {
+                       $missingExtensions[] = $extension;
+               }
+       }
+
+       if ( $missingExtensions ) {
+               wfMissingExtensions( $entryPoint, $mwVersion, $missingExtensions );
+       }
 }
 
 /**
@@ -107,7 +130,7 @@ function wfGenericError( $type, $mwVersion, $title, $shortText, $longText, $long
                                padding: 2em;
                                text-align: center;
                        }
-                       p, img, h1, h2 {
+                       p, img, h1, h2, ul  {
                                text-align: left;
                                margin: 0.5em 0 1em;
                        }
@@ -201,3 +224,38 @@ HTML;
 
        wfGenericError( $type, $mwVersion, 'External dependencies', $shortText, $longText, $longHtml );
 }
+
+/**
+ * Display an error for a PHP extension not existing.
+ *
+ * @param string $type See wfGenericError
+ * @param string $mwVersion See wfGenericError
+ * @param array $missingExts The extensions we're missing
+ */
+function wfMissingExtensions( $type, $mwVersion, $missingExts ) {
+       $shortText = "Installing some PHP extensions is required.";
+
+       $missingExtText = '';
+       $missingExtHtml = '';
+       $baseUrl = 'https://secure.php.net';
+       foreach ( $missingExts as $ext ) {
+               $missingExtText .= " * $ext <$baseUrl/$ext>\n";
+               $missingExtHtml .= "<li><b>$ext</b> "
+                       . "(<a href=\"$baseUrl/$ext\">more information</a>)</li>";
+       }
+
+       $cliText = "Error: Missing one or more required components of PHP.\n"
+               . "You are missing a required extension to PHP that MediaWiki needs.\n"
+               . "Please install:\n" . $missingExtText;
+
+       $longHtml = <<<HTML
+               You are missing a required extension to PHP that MediaWiki
+               requires to run. Please install:
+               <ul>
+               $missingExtHtml
+               </ul>
+HTML;
+
+       wfGenericError( $type, $mwVersion, 'Required components', $shortText,
+               $cliText, $longHtml );
+}
diff --git a/includes/PHPVersionError.php b/includes/PHPVersionError.php
deleted file mode 100644 (file)
index 9fbcf89..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-/**
- * Backwards compatibility. The PHP version error function is now
- * included in PHPVersionCheck.php.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @deprecated 1.25
- * @file
- */
-// @codingStandardsIgnoreStart MediaWiki.Usage.DirUsage.FunctionFound
-require_once dirname( __FILE__ ) . '/PHPVersionCheck.php';
-// @codingStandardsIgnoreEnd
index 0e45b25..eda8989 100644 (file)
@@ -720,11 +720,28 @@ class Revision implements IDBAccessObject {
        /**
         * Set the revision ID
         *
+        * This should only be used for proposed revisions that turn out to be null edits
+        *
         * @since 1.19
         * @param int $id
         */
        public function setId( $id ) {
-               $this->mId = $id;
+               $this->mId = (int)$id;
+       }
+
+       /**
+        * Set the user ID/name
+        *
+        * This should only be used for proposed revisions that turn out to be null edits
+        *
+        * @since 1.28
+        * @param integer $id User ID
+        * @param string $name User name
+        */
+       public function setUserIdAndName( $id, $name ) {
+               $this->mUser = (int)$id;
+               $this->mUserText = $name;
+               $this->mOrigUserText = $name;
        }
 
        /**
index 2c78061..5877932 100644 (file)
@@ -45,6 +45,8 @@ if ( !isset( $wgVersion ) ) {
        die( 1 );
 }
 
+mb_internal_encoding( 'UTF-8' );
+
 // Set various default paths sensibly...
 $ps_default = Profiler::instance()->scopedProfileIn( $fname . '-defaults' );
 
index dd911d0..67939a0 100644 (file)
@@ -122,7 +122,7 @@ class ApiStashEdit extends ApiBase {
                if ( $user->pingLimiter( 'stashedit' ) ) {
                        $status = 'ratelimited';
                } elseif ( $dbw->lock( $key, __METHOD__, 1 ) ) {
-                       $status = self::parseAndStash( $page, $content, $user );
+                       $status = self::parseAndStash( $page, $content, $user, $params['summary'] );
                        $dbw->unlock( $key, __METHOD__ );
                } else {
                        $status = 'busy';
@@ -135,12 +135,13 @@ class ApiStashEdit extends ApiBase {
 
        /**
         * @param WikiPage $page
-        * @param Content $content
+        * @param Content $content Edit content
         * @param User $user
+        * @param string $summary Edit summary
         * @return integer ApiStashEdit::ERROR_* constant
         * @since 1.25
         */
-       public static function parseAndStash( WikiPage $page, Content $content, User $user ) {
+       public static function parseAndStash( WikiPage $page, Content $content, User $user, $summary ) {
                $cache = ObjectCache::getLocalClusterInstance();
                $logger = LoggerFactory::getInstance( 'StashEdit' );
 
@@ -152,7 +153,8 @@ class ApiStashEdit extends ApiBase {
                        $key = self::getStashKey( $title, $content, $user );
 
                        // Let extensions add ParserOutput metadata or warm other caches
-                       Hooks::run( 'ParserOutputStashForEdit', [ $page, $content, $editInfo->output ] );
+                       Hooks::run( 'ParserOutputStashForEdit',
+                               [ $page, $content, $editInfo->output, $summary, $user ] );
 
                        list( $stashInfo, $ttl, $code ) = self::buildStashValue(
                                $editInfo->pstContent,
@@ -179,77 +181,6 @@ class ApiStashEdit extends ApiBase {
                return self::ERROR_PARSE;
        }
 
-       /**
-        * Attempt to cache PST content and corresponding parser output in passing
-        *
-        * This method can be called when the output was already generated for other
-        * reasons. Parsing should not be done just to call this method, however.
-        * $pstOpts must be that of the user doing the edit preview. If $pOpts does
-        * not match the options of WikiPage::makeParserOptions( 'canonical' ), this
-        * will do nothing. Provided the values are cacheable, they will be stored
-        * in memcached so that final edit submission might make use of them.
-        *
-        * @param Page|Article|WikiPage $page Page title
-        * @param Content $content Proposed page content
-        * @param Content $pstContent The result of preSaveTransform() on $content
-        * @param ParserOutput $pOut The result of getParserOutput() on $pstContent
-        * @param ParserOptions $pstOpts Options for $pstContent (MUST be for prospective author)
-        * @param ParserOptions $pOpts Options for $pOut
-        * @param string $timestamp TS_MW timestamp of parser output generation
-        * @return bool Success
-        */
-       public static function stashEditFromPreview(
-               Page $page, Content $content, Content $pstContent, ParserOutput $pOut,
-               ParserOptions $pstOpts, ParserOptions $pOpts, $timestamp
-       ) {
-               $cache = ObjectCache::getLocalClusterInstance();
-               $logger = LoggerFactory::getInstance( 'StashEdit' );
-
-               // getIsPreview() controls parser function behavior that references things
-               // like user/revision that don't exists yet. The user/text should already
-               // be set correctly by callers, just double check the preview flag.
-               if ( !$pOpts->getIsPreview() ) {
-                       return false; // sanity
-               } elseif ( $pOpts->getIsSectionPreview() ) {
-                       return false; // short-circuit (need the full content)
-               }
-
-               // PST parser options are for the user (handles signatures, etc...)
-               $user = $pstOpts->getUser();
-               // Get a key based on the source text, format, and user preferences
-               $title = $page->getTitle();
-               $key = self::getStashKey( $title, $content, $user );
-
-               // Parser output options must match cannonical options.
-               // Treat some options as matching that are different but don't matter.
-               $canonicalPOpts = $page->makeParserOptions( 'canonical' );
-               $canonicalPOpts->setIsPreview( true ); // force match
-               $canonicalPOpts->setTimestamp( $pOpts->getTimestamp() ); // force match
-               if ( !$pOpts->matches( $canonicalPOpts ) ) {
-                       $logger->info( "Uncacheable preview output for key '$key' ('$title') [options]." );
-                       return false;
-               }
-
-               // Set the time the output was generated
-               $pOut->setCacheTime( wfTimestampNow() );
-
-               // Build a value to cache with a proper TTL
-               list( $stashInfo, $ttl ) = self::buildStashValue( $pstContent, $pOut, $timestamp, $user );
-               if ( !$stashInfo ) {
-                       $logger->info( "Uncacheable parser output for key '$key' ('$title') [rev/TTL]." );
-                       return false;
-               }
-
-               $ok = $cache->set( $key, $stashInfo, $ttl );
-               if ( !$ok ) {
-                       $logger->error( "Failed to cache preview parser output for key '$key' ('$title')." );
-               } else {
-                       $logger->debug( "Cached preview output for key '$key'." );
-               }
-
-               return $ok;
-       }
-
        /**
         * Check that a prepared edit is in cache and still up-to-date
         *
@@ -418,6 +349,9 @@ class ApiStashEdit extends ApiBase {
                                ApiBase::PARAM_TYPE => 'text',
                                ApiBase::PARAM_REQUIRED => true
                        ],
+                       'summary' => [
+                               ApiBase::PARAM_TYPE => 'string',
+                       ],
                        'contentmodel' => [
                                ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
                                ApiBase::PARAM_REQUIRED => true
index 82a8349..cfd0c74 100644 (file)
        "apihelp-stashedit-param-contentmodel": "Content model of the new content.",
        "apihelp-stashedit-param-contentformat": "Content serialization format used for the input text.",
        "apihelp-stashedit-param-baserevid": "Revision ID of the base revision.",
+       "apihelp-stashedit-param-summary": "Change summary.",
 
        "apihelp-tag-description": "Add or remove change tags from individual revisions or log entries.",
        "apihelp-tag-param-rcid": "One or more recent changes IDs from which to add or remove the tag.",
index 11efd46..44769cf 100644 (file)
        "apihelp-stashedit-param-contentmodel": "{{doc-apihelp-param|stashedit|contentmodel}}",
        "apihelp-stashedit-param-contentformat": "{{doc-apihelp-param|stashedit|contentformat}}",
        "apihelp-stashedit-param-baserevid": "{{doc-apihelp-param|stashedit|baserevid}}",
+       "apihelp-stashedit-param-summary": "{{doc-apihelp-param|stashedit|summary}}",
        "apihelp-tag-description": "{{doc-apihelp-description|tag}}",
        "apihelp-tag-param-rcid": "{{doc-apihelp-param|tag|rcid}}",
        "apihelp-tag-param-revid": "{{doc-apihelp-param|tag|revid}}",
index 099a295..4a0f566 100644 (file)
@@ -369,6 +369,7 @@ class EnhancedChangesList extends ChangesList {
                ) {
                        $lineParams['classes'] = [ 'mw-enhanced-watched' ];
                }
+
                $separator = ' <span class="mw-changeslist-separator">. .</span> ';
 
                $data['recentChangesFlags'] = [
@@ -442,7 +443,7 @@ class EnhancedChangesList extends ChangesList {
 
                // give the hook a chance to modify the data
                $success = Hooks::run( 'EnhancedChangesListModifyLineData',
-                       [ $this, &$data, $block, $rcObj ] );
+                       [ $this, &$data, $block, $rcObj, &$classes ] );
                if ( !$success ) {
                        // skip entry if hook aborted it
                        return [];
index b8bd747..2f17729 100644 (file)
@@ -27,23 +27,28 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
        protected $page;
        /** @var integer */
        protected $pageId;
+       /** @var string */
+       protected $timestamp;
 
        /**
         * @param WikiPage $page Page we are updating
         * @param integer|null $pageId ID of the page we are updating [optional]
+        * @param string|null $timestamp TS_MW timestamp of deletion
         * @throws MWException
         */
-       function __construct( WikiPage $page, $pageId = null ) {
+       function __construct( WikiPage $page, $pageId = null, $timestamp = null ) {
                parent::__construct( false ); // no implicit transaction
 
                $this->page = $page;
-               if ( $page->exists() ) {
+               if ( $pageId ) {
+                       $this->pageId = $pageId; // page ID at time of deletion
+               } elseif ( $page->exists() ) {
                        $this->pageId = $page->getId();
-               } elseif ( $pageId ) {
-                       $this->pageId = $pageId;
                } else {
                        throw new InvalidArgumentException( "Page ID not known. Page doesn't exist?" );
                }
+
+               $this->timestamp = $timestamp ?: wfTimestampNow();
        }
 
        public function doUpdate() {
@@ -135,7 +140,9 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                                [
                                        'rc_type != ' . RC_LOG,
                                        'rc_namespace' => $title->getNamespace(),
-                                       'rc_title' => $title->getDBkey()
+                                       'rc_title' => $title->getDBkey(),
+                                       'rc_timestamp < ' .
+                                               $this->mDb->addQuotes( $this->mDb->timestamp( $this->timestamp ) )
                                ],
                                __METHOD__
                        );
@@ -188,7 +195,7 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                        'wiki' => $this->mDb->getWikiID(),
                        'job'  => new JobSpecification(
                                'deleteLinks',
-                               [ 'pageId' => $this->pageId ],
+                               [ 'pageId' => $this->pageId, 'timestamp' => $this->timestamp ],
                                [ 'removeDuplicates' => true ],
                                $this->page->getTitle()
                        )
index 7c161ca..4d5aa7a 100644 (file)
@@ -121,8 +121,6 @@ abstract class Installer {
        protected $envChecks = [
                'envCheckDB',
                'envCheckBrokenXML',
-               'envCheckMbstring',
-               'envCheckXML',
                'envCheckPCRE',
                'envCheckMemory',
                'envCheckCache',
@@ -136,9 +134,6 @@ abstract class Installer {
                'envCheckUploadsDirectory',
                'envCheckLibicu',
                'envCheckSuhosinMaxValueLength',
-               'envCheckCtype',
-               'envCheckIconv',
-               'envCheckJSON',
        ];
 
        /**
@@ -791,40 +786,6 @@ abstract class Installer {
                return true;
        }
 
-       /**
-        * Environment check for mbstring.func_overload.
-        * @return bool
-        */
-       protected function envCheckMbstring() {
-               if ( wfIniGetBool( 'mbstring.func_overload' ) ) {
-                       $this->showError( 'config-mbstring' );
-
-                       return false;
-               }
-
-               if ( !function_exists( 'mb_substr' ) ) {
-                       $this->showError( 'config-mbstring-absent' );
-
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
-        * Environment check for the XML module.
-        * @return bool
-        */
-       protected function envCheckXML() {
-               if ( !function_exists( "utf8_encode" ) ) {
-                       $this->showError( 'config-xml-bad' );
-
-                       return false;
-               }
-
-               return true;
-       }
-
        /**
         * Environment check for the PCRE module.
         *
@@ -1177,45 +1138,6 @@ abstract class Installer {
                }
        }
 
-       /**
-        * @return bool
-        */
-       protected function envCheckCtype() {
-               if ( !function_exists( 'ctype_digit' ) ) {
-                       $this->showError( 'config-ctype' );
-
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
-        * @return bool
-        */
-       protected function envCheckIconv() {
-               if ( !function_exists( 'iconv' ) ) {
-                       $this->showError( 'config-iconv' );
-
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
-        * @return bool
-        */
-       protected function envCheckJSON() {
-               if ( !function_exists( 'json_decode' ) ) {
-                       $this->showError( 'config-json' );
-
-                       return false;
-               }
-
-               return true;
-       }
-
        /**
         * Environment prep for the server hostname.
         */
index 9077082..2b7886a 100644 (file)
        "config-no-db": "Could not find a suitable database driver! You need to install a database driver for PHP.\nThe following database {{PLURAL:$2|type is|types are}} supported: $1.\n\nIf you compiled PHP yourself, reconfigure it with a database client enabled, for example, using <code>./configure --with-mysqli</code>.\nIf you installed PHP from a Debian or Ubuntu package, then you also need to install, for example, the <code>php5-mysql</code> package.",
        "config-outdated-sqlite": "<strong>Warning:</strong> you have SQLite $1, which is lower than minimum required version $2. SQLite will be unavailable.",
        "config-no-fts3": "<strong>Warning:</strong> SQLite is compiled without the [//sqlite.org/fts3.html FTS3 module], search features will be unavailable on this backend.",
-       "config-mbstring": "<strong>Fatal: [http://www.php.net/manual/en/ref.mbstring.php#mbstring.overload mbstring.func_overload] is active!</strong>\nThis option causes errors and may corrupt data unpredictably.\nYou cannot install or use MediaWiki unless this option is disabled.",
-       "config-xml-bad": "PHP's XML module is missing.\nMediaWiki requires functions in this module and will not work in this configuration.\nYou may need to install the php-xml RPM package.",
        "config-pcre-old": "<strong>Fatal:</strong> PCRE $1 or later is required.\nYour PHP binary is linked with PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE More information].",
        "config-pcre-no-utf8": "<strong>Fatal:</strong> PHP's PCRE module seems to be compiled without PCRE_UTF8 support.\nMediaWiki requires UTF-8 support to function correctly.",
        "config-memory-raised": "PHP's <code>memory_limit</code> is $1, raised to $2.",
        "config-memory-bad": "<strong>Warning:</strong> PHP's <code>memory_limit</code> is $1.\nThis is probably too low.\nThe installation may fail!",
-       "config-ctype": "<strong>Fatal:</strong> PHP must be compiled with support for the [http://www.php.net/manual/en/ctype.installation.php Ctype extension].",
-       "config-iconv": "<strong>Fatal:</strong> PHP must be compiled with support for the [http://www.php.net/manual/en/iconv.installation.php iconv extension].",
-       "config-json": "<strong>Fatal:</strong> PHP was compiled without JSON support.\nYou must install either the PHP JSON extension or the [http://pecl.php.net/package/jsonc PECL jsonc] extension before installing MediaWiki.\n* The PHP extension is included in Red Hat Enterprise Linux (CentOS) 5 and 6, though must be enabled in <code>/etc/php.ini</code> or <code>/etc/php.d/json.ini</code>.\n* Some Linux distributions released after May 2013 omit the PHP extension, instead packaging the PECL extension as <code>php5-json</code> or <code>php-pecl-jsonc</code>.",
-       "config-mbstring-absent": "<strong>Fatal:</strong> PHP must be compiled with support for the [http://www.php.net/manual/en/mbstring.setup.php mbstring extension].",
        "config-xcache": "[http://xcache.lighttpd.net/ XCache] is installed",
        "config-apc": "[http://www.php.net/apc APC] is installed",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] is installed",
index b2ff103..66a24c0 100644 (file)
        "config-no-db": "{{doc-important|Do not translate \"<code>./configure --with-mysqli</code>\" and \"<code>php5-mysql</code>\".}}\nParameters:\n* $1 is comma separated list of database types supported by MediaWiki.\n* $2 is the count of items in $1 - for use in plural.",
        "config-outdated-sqlite": "Used as warning. Parameters:\n* $1 - the version of SQLite that has been installed\n* $2 - minimum version",
        "config-no-fts3": "A \"[[:wikipedia:Front and back ends|backend]]\" is a system or component that ordinary users don't interact with directly and don't need to know about, and that is responsible for a distinct task or service - for example, a storage back-end is a generic system for storing data which other applications can use. Possible alternatives for back-end are \"system\" or \"service\", or (depending on context and language) even leave it untranslated.",
-       "config-mbstring": "{{Related|Config-fatal}}",
-       "config-xml-bad": "Status message in the MediaWiki installer environment checks.",
        "config-pcre-old": "Parameters:\n* $1 - minimum PCRE version number\n* $2 - the installed version of [[wikipedia:PCRE|PCRE]]\n{{Related|Config-fatal}}",
        "config-pcre-no-utf8": "PCRE is a name of a programmers' library for supporting regular expressions. It can probably be translated without change.\n{{Related|Config-fatal}}",
        "config-memory-raised": "Parameters:\n* $1 is the configured <code>memory_limit</code>.\n* $2 is the value to which <code>memory_limit</code> was raised.",
        "config-memory-bad": "Parameters:\n* $1 is the configured <code>memory_limit</code>.",
-       "config-ctype": "Message if support for [http://www.php.net/manual/en/ctype.installation.php Ctype] is missing from PHP.\n{{Related|Config-fatal}}",
-       "config-iconv": "Message if support for [http://www.php.net/manual/en/iconv.installation.php iconv] is missing from PHP.\n{{Related|Config-fatal}}",
-       "config-json": "Message if support for [[wikipedia:JSON|JSON]] is missing from PHP.\n* \"[[wikipedia:Red Hat Enterprise Linux|Red Hat Enterprise Linux]]\" (RHEL) and \"[[wikipedia:CentOS|CentOS]]\" refer to two almost-identical Linux distributions. \"5 and 6\" refers to version 5 or 6 of either distribution. Because RHEL 7 likely will not include the PHP extension, do not translate as \"5 or newer\".\n* \"The [http://www.php.net/json PHP extension]\" is the JSON extension included with PHP 5.2 and newer.\n* \"The [http://pecl.php.net/package/jsonc PECL extension]\" is based on the PHP extension, though excludes code some distributions have found unacceptable (see [[phab:T49431]]).\n{{Related|Config-fatal}}",
-       "config-mbstring-absent": "Message if support for [http://www.php.net/manual/en/mbstring.installation.php mbstring] is missing from PHP.\n{{Related|Config-fatal}}",
        "config-xcache": "Message indicates if this program is available",
        "config-apc": "Message indicates if this program is available",
        "config-wincache": "Message indicates if this program is available",
index e5357ce..ca5d534 100644 (file)
@@ -48,8 +48,10 @@ class DeleteLinksJob extends Job {
                        return false;
                }
 
+               $timestamp = isset( $this->params['timestamp'] ) ? $this->params['timestamp'] : null;
+
                $page = WikiPage::factory( $this->title ); // title when deleted
-               $update = new LinksDeletionUpdate( $page, $pageId );
+               $update = new LinksDeletionUpdate( $page, $pageId, $timestamp );
                DataUpdate::runUpdates( [ $update ] );
 
                return true;
index 5fbdcc7..78b0296 100644 (file)
@@ -418,12 +418,12 @@ class FormatMetadata extends ContextSource {
 
                                        case 'Flash':
                                                $flashDecode = [
-                                                       'fired' => $val & bindec( '00000001' ),
-                                                       'return' => ( $val & bindec( '00000110' ) ) >> 1,
-                                                       'mode' => ( $val & bindec( '00011000' ) ) >> 3,
-                                                       'function' => ( $val & bindec( '00100000' ) ) >> 5,
-                                                       'redeye' => ( $val & bindec( '01000000' ) ) >> 6,
-                                                       // 'reserved' => ( $val & bindec( '10000000' ) ) >> 7,
+                                                       'fired' => $val & 0b00000001,
+                                                       'return' => ( $val & 0b00000110 ) >> 1,
+                                                       'mode' => ( $val & 0b00011000 ) >> 3,
+                                                       'function' => ( $val & 0b00100000 ) >> 5,
+                                                       'redeye' => ( $val & 0b01000000 ) >> 6,
+                                                       // 'reserved' => ( $val & 0b10000000 ) >> 7,
                                                ];
                                                $flashMsgs = [];
                                                # We do not need to handle unknown values since all are used.
index a3c3ece..8d9d5a8 100644 (file)
@@ -1794,8 +1794,12 @@ class WikiPage implements Page, IDBAccessObject {
                        $this->mTimestamp = $now;
                } else {
                        // Bug 32948: revision ID must be set to page {{REVISIONID}} and
-                       // related variables correctly
+                       // related variables correctly. Likewise for {{REVISIONUSER}} (T135261).
                        $revision->setId( $this->getLatest() );
+                       $revision->setUserIdAndName(
+                               $this->getUser( Revision::RAW ),
+                               $this->getUserText( Revision::RAW )
+                       );
                }
 
                if ( $changed ) {
@@ -2071,7 +2075,7 @@ class WikiPage implements Page, IDBAccessObject {
                } else {
                        $edit->timestamp = wfTimestampNow();
                }
-               // @note: $cachedEdit is not used if the rev ID was referenced in the text
+               // @note: $cachedEdit is safely not used if the rev ID was referenced in the text
                $edit->revid = $revid;
 
                if ( $cachedEdit ) {
@@ -2164,17 +2168,26 @@ class WikiPage implements Page, IDBAccessObject {
                ];
                $content = $revision->getContent();
 
-               // Parse the text
-               // Be careful not to do pre-save transform twice: $text is usually
-               // already pre-save transformed once.
-               if ( !$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) {
-                       wfDebug( __METHOD__ . ": No prepared edit or vary-revision is set...\n" );
-                       $editInfo = $this->prepareContentForEdit( $content, $revision, $user );
+               // See if the parser output before $revision was inserted is still valid
+               $editInfo = false;
+               if ( !$this->mPreparedEdit ) {
+                       wfDebug( __METHOD__ . ": No prepared edit...\n" );
+               } elseif ( $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) {
+                       wfDebug( __METHOD__ . ": Prepared edit has vary-revision...\n" );
+               } elseif ( $this->mPreparedEdit->output->getFlag( 'vary-user' ) && !$options['changed'] ) {
+                       wfDebug( __METHOD__ . ": Prepared edit has vary-user and is null...\n" );
                } else {
-                       wfDebug( __METHOD__ . ": No vary-revision, using prepared edit...\n" );
+                       wfDebug( __METHOD__ . ": Using prepared edit...\n" );
                        $editInfo = $this->mPreparedEdit;
                }
 
+               if ( !$editInfo ) {
+                       // Parse the text again if needed. Be careful not to do pre-save transform twice:
+                       // $text is usually already pre-save transformed once. Avoid using the edit stash
+                       // as any prepared content from there or in doEditContent() was already rejected.
+                       $editInfo = $this->prepareContentForEdit( $content, $revision, $user, null, false );
+               }
+
                // Save it to the parser cache.
                // Make sure the cache time matches page_touched to avoid double parsing.
                ParserCache::singleton()->save(
index 116b274..6c84623 100644 (file)
@@ -2648,16 +2648,12 @@ class Parser {
                                break;
                        case 'revisionuser':
                                # Let the edit saving system know we should parse the page
-                               # *after* a revision ID has been assigned. This is for null edits.
-                               $this->mOutput->setFlag( 'vary-revision' );
-                               wfDebug( __METHOD__ . ": {{REVISIONUSER}} used, setting vary-revision...\n" );
+                               # *after* a revision ID has been assigned for null edits.
+                               $this->mOutput->setFlag( 'vary-user' );
+                               wfDebug( __METHOD__ . ": {{REVISIONUSER}} used, setting vary-user...\n" );
                                $value = $this->getRevisionUser();
                                break;
                        case 'revisionsize':
-                               # Let the edit saving system know we should parse the page
-                               # *after* a revision ID has been assigned. This is for null edits.
-                               $this->mOutput->setFlag( 'vary-revision' );
-                               wfDebug( __METHOD__ . ": {{REVISIONSIZE}} used, setting vary-revision...\n" );
                                $value = $this->getRevisionSize();
                                break;
                        case 'namespace':
@@ -3129,6 +3125,7 @@ class Parser {
                                        && $this->mOptions->getAllowSpecialInclusion()
                                        && $this->ot['html']
                                ) {
+                                       $specialPage = SpecialPageFactory::getPage( $title->getDBkey() );
                                        // Pass the template arguments as URL parameters.
                                        // "uselang" will have no effect since the Language object
                                        // is forced to the one defined in ParserOptions.
@@ -3147,7 +3144,12 @@ class Parser {
                                        $context = new RequestContext;
                                        $context->setTitle( $title );
                                        $context->setRequest( new FauxRequest( $pageArgs ) );
-                                       $context->setUser( $this->getUser() );
+                                       if ( $specialPage && $specialPage->maxIncludeCacheTime() === 0 ) {
+                                               $context->setUser( $this->getUser() );
+                                       } else {
+                                               // If this page is cached, then we better not be per user.
+                                               $context->setUser( User::newFromName( '127.0.0.1', false ) );
+                                       }
                                        $context->setLanguage( $this->mOptions->getUserLangObj() );
                                        $ret = SpecialPageFactory::capturePath( $title, $context );
                                        if ( $ret ) {
@@ -3155,7 +3157,9 @@ class Parser {
                                                $this->mOutput->addOutputPageMetadata( $context->getOutput() );
                                                $found = true;
                                                $isHTML = true;
-                                               $this->disableCache();
+                                               if ( $specialPage && $specialPage->maxIncludeCacheTime() !== false ) {
+                                                       $this->mOutput->updateCacheExpiry( $specialPage->maxIncludeCacheTime() );
+                                               }
                                        }
                                } elseif ( MWNamespace::isNonincludable( $title->getNamespace() ) ) {
                                        $found = false; # access denied
@@ -5637,7 +5641,7 @@ class Parser {
                        # will change the size.
                        if ( $revObject ) {
                                $this->mRevisionSize = $revObject->getSize();
-                       } elseif ( $this->ot['wiki'] || $this->mOptions->getIsPreview() ) {
+                       } else {
                                $this->mRevisionSize = $this->mInputSize;
                        }
                }
index 133729a..27b4f32 100644 (file)
@@ -1025,6 +1025,7 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                                'rememberMe' => [
                                        // option for saving the user token to a cookie
                                        'type' => 'check',
+                                       'name' => 'wpRemember',
                                        'label-message' => $this->msg( 'userlogin-remembermypassword' )
                                                ->numParams( $expirationDays ),
                                        'id' => 'wpRemember',
@@ -1032,7 +1033,6 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                                'loginattempt' => [
                                        // submit button
                                        'type' => 'submit',
-                                       'name' => 'wpRemember',
                                        'default' => $this->msg( 'pt-login-' . $continuePart . 'button' )->text(),
                                        'id' => 'wpLoginAttempt',
                                        'weight' => 100,
index 408c726..6624414 100644 (file)
@@ -175,6 +175,25 @@ class SpecialPage {
                return $this->mIncludable;
        }
 
+       /**
+        * How long to cache page when it is being included.
+        *
+        * @note If cache time is not 0, then the current user becomes an anon
+        *   if you want to do any per-user customizations, than this method
+        *   must be overriden to return 0.
+        * @since 1.26
+        * @return int Time in seconds, 0 to disable caching altogether,
+        *  false to use the parent page's cache settings
+        */
+       public function maxIncludeCacheTime() {
+               global $wgMiserMode;
+               if ( !$wgMiserMode ) {
+                       return 0;
+               } else {
+                       return 60*60;
+               }
+       }
+
        /**
         * Whether the special page is being evaluated via transclusion
         * @param bool $x
index c24b054..ff9a899 100644 (file)
@@ -478,4 +478,18 @@ class SpecialNewpages extends IncludableSpecialPage {
        protected function getGroupName() {
                return 'changes';
        }
+
+       /**
+        * How long to cache page when it is being included.
+        *
+        * @return int Time in seconds, 0 to disable caching altogether
+        */
+       public function maxIncludeCacheTime() {
+               global $wgMiserMode;
+               if ( !$wgMiserMode ) {
+                       return 0;
+               } else {
+                       return 60*5;
+               }
+       }
 }
index b6398cb..36ccd4a 100644 (file)
@@ -794,4 +794,19 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
        public function isIncludable() {
                return true;
        }
+
+       /**
+        * How long to cache page when it is being included.
+        *
+        * @return int|bool Time in seconds, 0 to disable caching altogether
+        */
+       public function maxIncludeCacheTime() {
+               global $wgMiserMode;
+               if ( !$wgMiserMode ) {
+                       return 0;
+               } else {
+                       return 60*5;
+               }
+       }
+
 }
index 0a6ccd3..f0c7d76 100644 (file)
@@ -30,8 +30,6 @@ if ( !defined( 'MEDIAWIKI' ) ) {
        exit( 1 );
 }
 
-mb_internal_encoding( 'UTF-8' );
-
 use CLDRPluralRuleParser\Evaluator;
 
 /**
index cc58b13..0d7d047 100644 (file)
        "upload-copy-upload-invalid-domain": "Copy uploads are not available from this domain.",
        "upload-foreign-cant-upload": "This wiki is not configured to upload files to the requested foreign file repository.",
        "upload-foreign-cant-load-config": "Loading file upload configuration for the foreign file repository failed.",
+       "upload-dialog-disabled": "File uploads using this dialog are disabled on this wiki.",
        "upload-dialog-title": "Upload file",
        "upload-dialog-button-cancel": "Cancel",
        "upload-dialog-button-done": "Done",
index dfe755e..7dbacb1 100644 (file)
        "upload-copy-upload-invalid-domain": "Error message shown if a user is trying to upload (i.e. copy) a file from a website that is not in $wgCopyUploadsDomains (if set).\n\nSee also:\n* {{msg-mw|http-invalid-url}}\n* {{msg-mw|tmp-create-error}}\n* {{msg-mw|tmp-write-error}}",
        "upload-foreign-cant-upload": "Error message shown when a user is trying to upload a file to foreign repository that is not configured to receive file uploads from current wiki.",
        "upload-foreign-cant-load-config": "Error message shown when a user is trying to upload a file to foreign repository and the foreign wiki is down or otherwise unable to respond to API requests.",
+       "upload-dialog-disabled": "Message shown when the upload dialog functionality is disabled. (This doesn't mean that uploads in general are disabled, only this specific method of uploading.)",
        "upload-dialog-title": "Title of the upload dialog box\n{{Identical|Upload file}}",
        "upload-dialog-button-cancel": "Button to cancel the dialog\n{{Identical|Cancel}}",
        "upload-dialog-button-done": "Button to close the dialog once upload is complete\n{{Identical|Done}}",
index a08297a..27d8161 100644 (file)
  */
 
 // Bail on old versions of PHP, or if composer has not been run yet to install
-// dependencies. Using dirname( __FILE__ ) here because __DIR__ is PHP5.3+.
-// @codingStandardsIgnoreStart MediaWiki.Usage.DirUsage.FunctionFound
-require_once dirname( __FILE__ ) . '/../includes/PHPVersionCheck.php';
-// @codingStandardsIgnoreEnd
+// dependencies.
+require_once __DIR__ . '/../includes/PHPVersionCheck.php';
 wfEntryPointCheck( 'cli' );
 
 /**
index 7304a2a..c91d824 100644 (file)
@@ -244,11 +244,11 @@ class FindHooks extends Maintenance {
                                // Comma for second argument
                                '(?:\s*(,))?' .
                                // Second argument must start with array to be processed
-                               '(?:\s*array\s*\(' .
+                               '(?:\s*(?:array\s*\(|\[)' .
                                // Matching inside array - allows one deep of brackets
-                               '((?:[^\(\)]|\([^\(\)]*\))*)' .
+                               '((?:[^\(\)\[\]]|\((?-1)\)|\[(?-1)\])*)' .
                                // End
-                               '\))?/',
+                               '[\)\]])?/',
                        $content,
                        $m,
                        PREG_SET_ORDER
index ae3074a..3e632f0 100644 (file)
  * @ingroup Maintenance
  */
 
-// Checking for old versions of PHP is done in Maintenance.php
-// We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+
-// @codingStandardsIgnoreStart MediaWiki.Usage.DirUsage.FunctionFound
-require_once dirname( __FILE__ ) . '/Maintenance.php';
-// @codingStandardsIgnoreEnd
+require_once __DIR__ . '/Maintenance.php';
 
 define( 'MW_CONFIG_CALLBACK', 'Installer::overrideConfig' );
 define( 'MEDIAWIKI_INSTALL', true );
index 775fa7b..b96e7eb 100755 (executable)
@@ -25,7 +25,6 @@
  * @ingroup Maintenance
  */
 
-$wgUseMasterForMaintenance = true;
 require_once __DIR__ . '/Maintenance.php';
 
 /**
@@ -83,13 +82,6 @@ class UpdateMediaWiki extends Maintenance {
                                "ABORTING (see https://bugs.php.net/bug.php?id=45996).\n",
                                true );
                }
-
-               if ( !function_exists( 'mb_strlen' ) ) {
-                       $this->error(
-                               "MediaWiki now requires the mbstring PHP extension, your system doesn't have it.\n"
-                               . "ABORTING.\n",
-                               true );
-               }
        }
 
        function execute() {
index 1e80c26..8130b26 100644 (file)
@@ -1183,6 +1183,7 @@ return [
                ],
                'messages' => [
                        'uploaddisabledtext',
+                       'upload-dialog-disabled',
                        'upload-foreign-cant-upload',
                ]
        ],
index d354fc2..297f814 100644 (file)
@@ -30,6 +30,7 @@
                                        section: data.wpSection,
                                        sectiontitle: '',
                                        text: data.wpTextbox1,
+                                       summary: data.wpSummary,
                                        contentmodel: data.model,
                                        contentformat: data.format,
                                        baserevid: data.parentRevId
@@ -45,7 +46,7 @@
                        return newText !== data.wpTextbox1;
                }
 
-               function onTextChanged() {
+               function onEditorIdle() {
                        if ( !isChanged() ) {
                                return;
                        }
                        stashEdit();
                }
 
-               function onTextKeyPress( e ) {
+               function onTextKeyUp( e ) {
                        // Ignore keystrokes that don't modify text, like cursor movements.
-                       // See <http://stackoverflow.com/q/2284844>.
-                       if ( e.which === 0 ) {
+                       // See <http://www.javascripter.net/faq/keycodes.htm> and
+                       // <http://www.quirksmode.org/js/keys.html>. We don't have to be
+                       // exhaustive, because the cost of misfiring is low.
+                       if ( ( e.which >= 33 && e.which <= 40 ) || ( e.which >= 16 && e.which <= 18 ) ) {
                                return;
                        }
 
                        clearTimeout( timer );
-
-                       if ( pending ) {
-                               pending.abort();
-                       }
-
-                       timer = setTimeout( onTextChanged, idleTimeout );
+                       timer = setTimeout( onEditorIdle, idleTimeout );
                }
 
                function onFormLoaded() {
-                       // Reverts may involve use (undo) links; stash as they review the diff.
-                       // Since the form has a pre-filled summary, stash the edit immediately.
-                       if ( mw.util.getParamValue( 'undo' ) !== null ) {
+                       if (
+                               // Reverts may involve use (undo) links; stash as they review the diff.
+                               // Since the form has a pre-filled summary, stash the edit immediately.
+                               mw.util.getParamValue( 'undo' ) !== null
+                               // Pressing "show changes" and "preview" also signify that the user will
+                               // probably save the page soon
+                               || $.inArray( $form.find( '#mw-edit-mode' ).val(), [ 'preview', 'diff' ] ) > -1
+                       ) {
                                stashEdit();
                        }
                }
@@ -84,8 +87,8 @@
                        return;
                }
 
-               $text.on( { change: onTextChanged, keypress: onTextKeyPress } );
-               $summary.on( { focus: onTextChanged } );
+               $text.on( { change: onEditorIdle, keyup: onTextKeyUp } );
+               $summary.on( { focus: onEditorIdle } );
                onFormLoaded();
 
        } );
index da356b1..ae3c01b 100644 (file)
@@ -89,6 +89,7 @@ div.searchresult {
 }
 #mw-search-top-table div.oo-ui-actionFieldLayout {
        float: left;
+       width: 100%;
 }
 fieldset#mw-searchoptions {
        margin: 0;
index eeeab68..781c1df 100644 (file)
@@ -43,7 +43,9 @@
                // However, if the target is a remote wiki, we must check the API
                // to confirm that the target is one that this site is configured to
                // support.
-               if ( this.target === 'local' ) {
+               if ( validTargets.length === 0 ) {
+                       this.apiPromise = $.Deferred().reject( 'upload-dialog-disabled' );
+               } else if ( this.target === 'local' ) {
                        // If local uploads were requested, but they are disabled, fail.
                        if ( !mw.config.get( 'wgEnableUploads' ) ) {
                                this.apiPromise = $.Deferred().reject( 'uploaddisabledtext' );
index 312e745..62ee94e 100644 (file)
@@ -3,7 +3,7 @@
  *
  * This file is where we decide whether to initialise the modern run-time.
  */
-/*jshint unused: false, evil: true */
+/*jshint unused: false */
 /*globals mw, RLQ: true, NORLQ: true, $VARS, $CODE, performance */
 
 var mediaWikiLoadStart = ( new Date() ).getTime(),
index 132743f..e519f59 100644 (file)
@@ -1137,6 +1137,19 @@ class ParserTest {
                        'fileExists' => true
                ], $this->db->timestamp( '20010115123500' ), $user );
 
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Audio.oga' ) );
+               $image->recordUpload2( '', 'An awesome hitsong', 'Will it play', [
+                       'size' => 12345,
+                       'width' => 0,
+                       'height' => 0,
+                       'bits' => 0,
+                       'media_type' => MEDIATYPE_AUDIO,
+                       'mime' => 'application/ogg',
+                       'metadata' => serialize( [] ),
+                       'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
+                       'fileExists' => true
+               ], $this->db->timestamp( '20010115123500' ), $user );
+
                # A DjVu file
                $image = wfLocalFile( Title::makeTitle( NS_FILE, 'LoremIpsum.djvu' ) );
                $image->recordUpload2( '', 'Upload a DjVu', 'A DjVu', [
@@ -1252,6 +1265,8 @@ class ParserTest {
                copy( "$IP/tests/phpunit/data/parser/LoremIpsum.djvu", "$dir/5/5f/LoremIpsum.djvu" );
                wfMkdirParents( $dir . '/0/00', null, __METHOD__ );
                copy( "$IP/tests/phpunit/data/parser/320x240.ogv", "$dir/0/00/Video.ogv" );
+               wfMkdirParents( $dir . '/4/41', null, __METHOD__ );
+               copy( "$IP/tests/phpunit/data/media/say-test.ogg", "$dir/4/41/Audio.oga" );
 
                return;
        }
@@ -1301,6 +1316,7 @@ class ParserTest {
                                "$dir/thumb/0/00/Video.ogv/270px--Video.ogv.jpg",
                                "$dir/thumb/0/00/Video.ogv/320px-seek=2-Video.ogv.jpg",
                                "$dir/thumb/0/00/Video.ogv/320px-seek=3.3666666666667-Video.ogv.jpg",
+                               "$dir/4/41/Audio.oga",
                        ]
                );
 
@@ -1330,6 +1346,8 @@ class ParserTest {
                                "$dir/thumb/5/5f",
                                "$dir/thumb/5",
                                "$dir/thumb",
+                               "$dir/4/41",
+                               "$dir/4",
                                "$dir/math/f/a/5",
                                "$dir/math/f/a",
                                "$dir/math/f",
index 1f1d53b..2e059d7 100644 (file)
@@ -7083,11 +7083,12 @@ parsoid=wt2html
 !! end
 
 !! test
-Strip unsupported table tags
+Strip unsupported table tags, but introduce row wikitext as required
 !! options
 parsoid=html2wt
 !! html/parsoid
 <table>
+<caption>Test</caption>
 <thead>
 <tr>
 <th>Month</th>
@@ -7113,10 +7114,12 @@ parsoid=html2wt
 </table>
 !! wikitext
 {|
+|+Test
 
 !Month
 !Savings
 
+|-
 |January
 |$100
 
@@ -7124,10 +7127,31 @@ parsoid=html2wt
 |February
 |$80
 
+|-
 |Sum
 |$180
 
 |}
+!! html/php+tidy
+<table>
+<caption>Test</caption>
+<tr>
+<th>Month</th>
+<th>Savings</th>
+</tr>
+<tr>
+<td>January</td>
+<td>$100</td>
+</tr>
+<tr>
+<td>February</td>
+<td>$80</td>
+</tr>
+<tr>
+<td>Sum</td>
+<td>$180</td>
+</tr>
+</table>
 !! end
 
 !! test
@@ -7243,6 +7267,56 @@ parsoid=html2wt
 </table>
 !! end
 
+!! test
+Serialize wikitext list items as HTML list items when embedded in a HTML list
+!! options
+parsoid=html2wt
+!! html
+<ul data-parsoid='{"stx": "html"}'>
+<li data-parsoid='{}'>a</li>
+<li>b</li>
+</ul>
+!! wikitext
+<ul>
+<li>a</li>
+<li>b</li>
+</ul>
+!! end
+
+# SSS FIXME: Is this actually a good thing given the
+# odd nested list output that is generated by MW?
+# <ul><li>foo<ul>..</ul></li></ul> instead of
+# <ul><li>foo</li><ul>..</ul></ul>
+!! test
+Wikitext lists can be nested inside HTML lists
+!! options
+parsoid=html2wt
+!! html
+<ul data-parsoid='{"stx": "html"}'>
+<li data-parsoid='{"stx": "html"}'>a
+<ul><li>b</li></ul>
+</li>
+</ul>
+
+<ul data-parsoid='{"stx": "html"}'>
+<li>x
+<ul><li>y</li></ul>
+</li>
+</ul>
+!! wikitext
+<ul>
+<li>a
+* b
+</li>
+</ul>
+
+<ul>
+<li>x
+* y
+</li>
+</ul>
+!! end
+
 ###
 ### Internal links
 ###
@@ -9870,7 +9944,7 @@ Magic Word: {{NUMBEROFFILES}}
 !! wikitext
 {{NUMBEROFFILES}}
 !! html
-<p>6
+<p>7
 </p>
 !! end
 
index 354ddd4..8512572 100644 (file)
@@ -324,7 +324,21 @@ class NewParserTest extends MediaWikiTestCase {
                        ], $this->db->timestamp( '20010115123500' ), $user );
                }
 
-               # A DjVu file
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Audio.oga' ) );
+               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
+                       $image->recordUpload2( '', 'An awesome hitsong ', 'Will it play', [
+                                       'size'        => 12345,
+                                       'width'       => 0,
+                                       'height'      => 0,
+                                       'bits'        => 0,
+                                       'media_type'  => MEDIATYPE_AUDIO,
+                                       'mime'        => 'application/ogg',
+                                       'metadata'    => serialize( [] ),
+                                       'sha1'        => Wikimedia\base_convert( '', 16, 36, 32 ),
+                                       'fileExists'  => true
+                       ], $this->db->timestamp( '20010115123500' ), $user );
+               }
+
                # A DjVu file
                $image = wfLocalFile( Title::makeTitle( NS_FILE, 'LoremIpsum.djvu' ) );
                if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
index b110e21..99992fe 100644 (file)
@@ -76,18 +76,30 @@ class MockOggHandler extends OggHandlerTMH {
        }
 
        function getLength( $file ) {
+               if ( $this->isAudio( $file ) ) {
+                       return 0.99875;
+               }
                return 4.3666666666667;
        }
 
        function getBitRate( $file ) {
+               if ( $this->isAudio( $file ) ) {
+                       return 41107;
+               }
                return 590013;
        }
 
        function getWebType( $file ) {
+               if ( $this->isAudio( $file ) ) {
+                       return "audio/ogg; codecs=\"vorbis\"";
+               }
                return "video/ogg; codecs=\"theora\"";
        }
 
        function getFramerate( $file ) {
+               if ( $this->isAudio( $file ) ) {
+                       return 0;
+               }
                return 30;
        }
 }
index 1091d09..79f37dc 100644 (file)
@@ -1,5 +1,4 @@
 /*global CompletenessTest, sinon */
-/*jshint evil: true */
 ( function ( $, mw, QUnit ) {
        'use strict';