Merge "Localisation updates for namespace names for core from http://translatewiki...
authorTim Starling <tstarling@wikimedia.org>
Mon, 26 Mar 2012 03:45:23 +0000 (03:45 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 26 Mar 2012 03:45:23 +0000 (03:45 +0000)
49 files changed:
.gitignore
CREDITS
README
RELEASE-NOTES-1.19
RELEASE-NOTES-1.20
api.php
docs/database.txt
extensions/.gitignore [new file with mode: 0644]
img_auth.php
includes/AutoLoader.php
includes/ChangeTags.php
includes/CryptRand.php
includes/Export.php
includes/GitInfo.php [new file with mode: 0644]
includes/HTMLForm.php
includes/RecentChange.php
includes/User.php
includes/db/Database.php
includes/db/DatabasePostgres.php
includes/filerepo/FileRepo.php
includes/filerepo/backend/FileBackendStore.php
includes/filerepo/file/LocalFile.php
includes/installer/DatabaseInstaller.php
includes/installer/PostgresUpdater.php
includes/objectcache/MemcachedClient.php
includes/profiler/Profiler.php
includes/profiler/ProfilerSimple.php
includes/specials/SpecialBlock.php
includes/specials/SpecialNewimages.php
includes/specials/SpecialPasswordReset.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialVersion.php
includes/specials/SpecialWatchlist.php
languages/Language.php
languages/classes/LanguageKaa.php
languages/messages/MessagesEn.php
maintenance/deleteDefaultMessages.php
maintenance/dumpTextPass.php
maintenance/language/messages.inc
maintenance/tables.sql
mw-config/index.php
profileinfo.php
resources/mediawiki.action/mediawiki.action.watch.ajax.js
resources/mediawiki.api/mediawiki.api.watch.js
skins/Vector.php
tests/phpunit/includes/RecentChangeTest.php
tests/phpunit/languages/LanguageTest.php
tests/selenium/installer/README.txt
thumb.php

index 6688707..943378a 100644 (file)
@@ -19,3 +19,5 @@ cache/*.cdb
 images/[0-9a-f]
 images/temp
 images/thumb
+maintenance/dev/data/
+maintenance/.mweval_history
\ No newline at end of file
diff --git a/CREDITS b/CREDITS
index 54402b0..d0c4623 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -199,4 +199,4 @@ following names for their contribution to the product.
 * Meno25
 * Rotem Liss
 * Shinjiman
-* Translatewiki.net Translators http://translatewiki.net/wiki/Special:ListUsers/translator
+* Translatewiki.net Translators https://translatewiki.net/wiki/Special:ListUsers/translator
diff --git a/README b/README
index 078f92c..48cf336 100644 (file)
--- a/README
+++ b/README
@@ -1,5 +1,3 @@
-2008-11-11
-
 For system requirements, installation and upgrade details, see the files 
 RELEASE-NOTES, INSTALL, and UPGRADE.
 
@@ -88,8 +86,8 @@ The official website for MediaWiki is located at:
 
        http://www.mediawiki.org/
 
-The code is currently maintained in a Subversion repository at
-svn.wikimedia.org. See http://www.mediawiki.org/wiki/Subversion for details.
+The code is currently maintained in a Git repository at
+gerrit.wikimedia.org. See http://www.mediawiki.org/wiki/Git for details.
 
 Please report bugs and make feature requests in our Bugzilla system:
 
index 576cfa8..9255909 100644 (file)
@@ -13,15 +13,15 @@ production.
 
 === Changes since 1.19 beta 1 ===
 * (bug 35014) Including a special page no longer sets the page's title to the
-  included page
-* (bug 35019) Edit summaries are no longer transformed in notification e-mails
-* (bug 35152) Help message for e-mail is shown again in user preferences
+  included page.
+* (bug 35019) Edit summaries are no longer transformed in notification e-mails.
+* (bug 35152) Help message for e-mail is shown again in user preferences.
 * (bug 34887) $3 and $4 parameters are now substituted correctly in message
-  "movepage-moved"
+  "movepage-moved".
 * (bug 34841) Edit links are no longer displayed when display old page versions
-* (bug 34889) User name should be normalized on Special:Contributions
-* (bug 35051) If heading has a trailing space after == then its name is not 
-  preloaded into edit summary on section edit
+* (bug 34889) User name should be normalized on Special:Contributions.
+* (bug 35051) If heading has a trailing space after == then its name is not
+  preloaded into edit summary on section edit.
 * (bug 31417) New ID mw-content-text around the actual page text, without categories,
   contentSub, ... The same div often also contains the class mw-content-ltr/rtl.
 * (bug 35303) Proxy and DNS blacklist blocking works again
@@ -31,6 +31,9 @@ production.
   core parser functions which operate on strings, such as padleft.
 * (bug 18295) Don't expose strip markers when a tag appears inside a link 
   inside a heading.
+* (bug 34907) Fixed exposure of tokens through load.php that could have facilitated
+  CSRF attacks
+* Special:Watchlist no longer sets links to feed when the user is anonymous
 
 === Configuration changes in 1.19 ===
 * Removed SkinTemplateSetupPageCss hook; use BeforePageDisplay instead.
@@ -45,6 +48,7 @@ production.
 * (bug 32239) Removed $wgEnableTooltipsAndAccesskeys.
 * Removed $wgVectorShowVariantName.
 * Removed $wgExtensionAliasesFiles. Use $wgExtensionMessagesFiles.
+* Removed $wgResourceLoaderInlinePrivateModules, now always enabled.
 
 === New features in 1.19 ===
 * (bug 19838) Add ability to get all interwiki prefixes also if the interwiki
@@ -128,8 +132,8 @@ production.
   200 status code instead of 404 for nonexistent articles.
 * (bug 33447) Link to the broken image tracking category from Special:Wantedfiles.
 * (bug 27724) Add timestamp to job queue.
-* (bug 30339) Implement SpecialPage for running javascript tests. Disabled by default, due to
-  tests potentially being harmful, not to be run on a production wiki.
+* (bug 30339) Implement SpecialPage for running javascript tests. Disabled by default,
+  due to tests potentially being harmful, not to be run on a production wiki.
   Enable by setting $wgEnableJavaScriptTest to true.
 * Extensions can use the RequestContextCreateSkin hook to override what skin is
   loaded in some contexts.
@@ -146,8 +150,8 @@ production.
 * Special:MovePage now has a dropdown menu for namespaces.
 * (bug 34420) Special:Version now shows git HEAD sha1 when available.
 * (bug 33952) Refactor mw.toolbar to allow dynamic additions at any time.
-* Now possible to specify separate section title and edit summary when adding a new section to a
-  page via the edit API action.
+* Now possible to specify separate section title and edit summary when adding
+  a new section to a page via the edit API action.
 
 === Bug fixes in 1.19 ===
 * $wgUploadNavigationUrl should be used for file redlinks if.
index bef93ee..d4533aa 100644 (file)
@@ -50,6 +50,7 @@ production.
 * (bug 34735) Updated compressOld.php documentation to mention the different
   usages of -s and -n parameters depending on compression type
 * (bug 13896) Rendering of devanagari numbers in automatic '#' number lists
+* (bug 18704) Add an unique CSS class or ID to the tagfilter table row at RecentChanges 
 
 === API changes in 1.20 ===
 * (bug 34316) Add ability to retrieve maximum upload size from MediaWiki API.
diff --git a/api.php b/api.php
index a5a2579..889c5f1 100644 (file)
--- a/api.php
+++ b/api.php
@@ -45,7 +45,7 @@ if ( !function_exists( 'version_compare' ) || version_compare( phpversion(), '5.
 
 // Initialise common code.
 if ( isset( $_SERVER['MW_COMPILED'] ) ) {
-       require ( 'phase3/includes/WebStart.php' );
+       require ( 'core/includes/WebStart.php' );
 } else {
        require ( dirname( __FILE__ ) . '/includes/WebStart.php' );
 }
index 70815d4..c0a2412 100644 (file)
@@ -8,7 +8,7 @@ By Tim Starling, January 2006.
 For information about the MediaWiki database layout, such as a 
 description of the tables and their contents, please see:
   http://www.mediawiki.org/wiki/Manual:Database_layout
-  http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/maintenance/tables.sql?view=markup
+  https://gerrit.wikimedia.org/r/gitweb?p=mediawiki/core.git;a=blob_plain;f=maintenance/tables.sql;hb=HEAD
 
 
 ------------------------------------------------------------------------
diff --git a/extensions/.gitignore b/extensions/.gitignore
new file mode 100644 (file)
index 0000000..f847620
--- /dev/null
@@ -0,0 +1,3 @@
+*
+!README
+!.gitignore
index 3999bf3..82afef2 100644 (file)
@@ -28,7 +28,7 @@
 
 define( 'MW_NO_OUTPUT_COMPRESSION', 1 );
 if ( isset( $_SERVER['MW_COMPILED'] ) ) {
-       require ( 'phase3/includes/WebStart.php' );
+       require ( 'core/includes/WebStart.php' );
 } else {
        require ( dirname( __FILE__ ) . '/includes/WebStart.php' );
 }
index 4ab5de8..e791873 100644 (file)
@@ -95,6 +95,7 @@ $wgAutoloadLocalClasses = array(
        'FormAction' => 'includes/Action.php',
        'FormOptions' => 'includes/FormOptions.php',
        'FormSpecialPage' => 'includes/SpecialPage.php',
+       'GitInfo' => 'includes/GitInfo.php',
        'HashtableReplacer' => 'includes/StringUtils.php',
        'HistoryBlob' => 'includes/HistoryBlob.php',
        'HistoryBlobCurStub' => 'includes/HistoryBlob.php',
index 63d3732..01b93ad 100644 (file)
@@ -210,7 +210,7 @@ class ChangeTags {
                        return $fullForm ? '' : array();
 
                $data = array( Html::rawElement( 'label', array( 'for' => 'tagfilter' ), wfMsgExt( 'tag-filter', 'parseinline' ) ),
-                       Xml::input( 'tagfilter', 20, $selected ) );
+                       Xml::input( 'tagfilter', 20, $selected ), array( 'class' => 'mw-tagfilter-input' ) );
 
                if ( !$fullForm ) {
                        return $data;
@@ -219,7 +219,7 @@ class ChangeTags {
                $html = implode( '&#160;', $data );
                $html .= "\n" . Xml::element( 'input', array( 'type' => 'submit', 'value' => wfMsg( 'tag-filter-submit' ) ) );
                $html .= "\n" . Html::hidden( 'title', $title->getPrefixedText() );
-               $html = Xml::tags( 'form', array( 'action' => $title->getLocalURL(), 'method' => 'get' ), $html );
+               $html = Xml::tags( 'form', array( 'action' => $title->getLocalURL(), 'class' => 'mw-tagfilter-form', 'method' => 'get' ), $html );
 
                return $html;
        }
index 10f379c..e4be1b3 100644 (file)
@@ -120,7 +120,7 @@ class MWCryptRand {
        /**
         * Randomly hash data while mixing in clock drift data for randomness
         *
-        * @param $data The data to randomly hash.
+        * @param $data string The data to randomly hash.
         * @return String The hashed bytes
         * @author Tim Starling
         */
@@ -166,7 +166,7 @@ class MWCryptRand {
 
        /**
         * Return a rolling random state initially build using data from unstable sources
-        * @return A new weak random state
+        * @return string A new weak random state
         */
        protected function randomState() {
                static $state = null;
@@ -184,6 +184,7 @@ class MWCryptRand {
 
        /**
         * Decide on the best acceptable hash algorithm we have available for hash()
+        * @throws MWException
         * @return String A hash algorithm
         */
        protected function hashAlgo() {
@@ -227,6 +228,7 @@ class MWCryptRand {
         * Generate an acceptably unstable one-way-hash of some text
         * making use of the best hash algorithm that we have available.
         *
+        * @param $data string
         * @return String A raw hash of the data
         */
        protected function hash( $data ) {
@@ -237,6 +239,8 @@ class MWCryptRand {
         * Generate an acceptably unstable one-way-hmac of some text
         * making use of the best hash algorithm that we have available.
         *
+        * @param $data string
+        * @param $key string
         * @return String A raw hash of the data
         */
        protected function hmac( $data, $key ) {
@@ -282,7 +286,7 @@ class MWCryptRand {
                                if ( $iv === false ) {
                                        wfDebug( __METHOD__ . ": mcrypt_create_iv returned false.\n" );
                                } else {
-                                       $bytes .= $iv;
+                                       $buffer .= $iv;
                                        wfDebug( __METHOD__ . ": mcrypt_create_iv generated " . strlen( $iv ) . " bytes of randomness.\n" );
                                }
                                wfProfileOut( __METHOD__ . '-mcrypt' );
@@ -409,6 +413,7 @@ class MWCryptRand {
 
        /**
         * Return a singleton instance of MWCryptRand
+        * @return MWCryptRand
         */
        protected static function singleton() {
                if ( is_null( self::$singleton ) ) {
index 35a1b5b..82aa946 100644 (file)
@@ -487,12 +487,6 @@ class XmlDumpWriter {
                        }
                }
 
-               if ( $row->rev_sha1 ) {
-                       $out .= "      " . Xml::element('sha1', null, strval( $row->rev_sha1 ) ) . "\n";
-               } else {
-                       $out .= "      <sha1/>\n";
-               }
-
                if ( $row->page_restrictions != '' ) {
                        $out .= '    ' . Xml::element( 'restrictions', array(),
                                strval( $row->page_restrictions ) ) . "\n";
@@ -560,6 +554,12 @@ class XmlDumpWriter {
                                "" ) . "\n";
                }
 
+               if ( $row->rev_sha1 && !( $row->rev_deleted & Revision::DELETED_TEXT ) ) {
+                       $out .= "      " . Xml::element('sha1', null, strval( $row->rev_sha1 ) ) . "\n";
+               } else {
+                       $out .= "      <sha1/>\n";
+               }
+
                wfRunHooks( 'XmlDumpWriterWriteRevision', array( &$this, &$out, $row, $text ) );
 
                $out .= "    </revision>\n";
diff --git a/includes/GitInfo.php b/includes/GitInfo.php
new file mode 100644 (file)
index 0000000..bc3f35e
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+/**
+ * A class to help return information about a git repo MediaWiki may be inside
+ * This is used by Special:Version and is also useful for the LocalSettings.php
+ * of anyone working on large branches in git to setup config that show up only
+ * when specific branches are currently checked out.
+ *
+ * @file
+ */
+
+class GitInfo {
+
+       /**
+        * Singleton for the repo at $IP
+        */
+       protected static $repo = null;
+
+       /**
+        * Location of the .git directory
+        */
+       protected $basedir;
+
+       /**
+        * @param $dir The root directory of the repo where the .git dir can be found
+        */
+       public function __construct( $dir ) {
+               $this->basedir = "{$dir}/.git/";
+       }
+
+       /**
+        * Return a singleton for the repo at $IP
+        * @return GitInfo
+        */
+       public static function repo() {
+               global $IP;
+               if ( is_null( self::$repo ) ) {
+                       self::$repo = new self( $IP );
+               }
+               return self::$repo;
+       }
+
+       /**
+        * Check if a string looks like a hex encoded SHA1 hash
+        *
+        * @param $str The string to check
+        * @return bool Whether or not the string looks like a SHA1
+        */
+       public static function isSHA1( $str ) {
+               return !!preg_match( '/^[0-9A-Z]{40}$/i', $str );
+       }
+
+       /**
+        * Return the HEAD of the repo (without any opening "ref: ")
+        * @return string The HEAD
+        */
+       public function getHead() {
+               $HEADfile = "{$this->basedir}/HEAD";
+
+               if ( !is_readable( $HEADfile ) ) {
+                       return false;
+               }
+
+               $HEAD = file_get_contents( $HEADfile );
+
+               if ( preg_match( "/ref: (.*)/", $HEAD, $m ) ) {
+                       return rtrim( $m[1] );
+               } else {
+                       return $HEAD;
+               }
+       }
+
+       /**
+        * Return the SHA1 for the current HEAD of the repo
+        * @return string A SHA1 or false
+        */
+       public function getHeadSHA1() {
+               $HEAD = $this->getHead();
+
+               // If detached HEAD may be a SHA1
+               if ( self::isSHA1( $HEAD ) ) {
+                       return $HEAD;
+               }
+
+               // If not a SHA1 it may be a ref:
+               $REFfile = "{$this->basedir}{$HEAD}";
+               if ( !is_readable( $REFfile ) ) {
+                       return false;
+               }
+
+               $sha1 = rtrim( file_get_contents( $REFfile ) );
+
+               return $sha1;
+       }
+
+       /**
+        * Return the name of the current branch, or HEAD if not found
+        * @return string The branch name, HEAD, or false
+        */
+       public function getCurrentBranch() {
+               $HEAD = $this->getHead();
+               if ( $HEAD && preg_match( "#^refs/heads/(.*)$#", $HEAD, $m ) ) {
+                       return $m[1];
+               } else {
+                       return $HEAD;
+               }
+       }
+
+       /**
+        * @see self::getHeadSHA1
+        */
+       public static function headSHA1() {
+               return self::repo()->getHeadSHA1();
+       }
+
+       /**
+        * @see self::getCurrentBranch
+        */
+       public static function currentBranch() {
+               return self::repo()->getCurrentBranch();
+       }
+
+}
\ No newline at end of file
index 3b3e1b6..dccf967 100644 (file)
@@ -271,7 +271,7 @@ class HTMLForm extends ContextSource {
 
        /**
         * The here's-one-I-made-earlier option: do the submission if
-        * posted, or display the form with or without funky valiation
+        * posted, or display the form with or without funky validation
         * errors
         * @return Bool or Status whether submission was successful.
         */
@@ -279,7 +279,7 @@ class HTMLForm extends ContextSource {
                $this->prepareForm();
 
                $result = $this->tryAuthorizedSubmit();
-               if ( $result === true || ( $result instanceof Status && $result->isGood() ) ){
+               if ( $result === true || ( $result instanceof Status && $result->isGood() ) ) {
                        return $result;
                }
 
index a1097be..e57efae 100644 (file)
@@ -751,7 +751,7 @@ class RecentChange {
                return ChangesList::showCharacterDifference( $old, $new );
        }
 
-       public static function checkIPAddress( $ip ) {
+       private static function checkIPAddress( $ip ) {
                global $wgRequest;
                if ( $ip ) {
                        if ( !IP::isIPAddress( $ip ) ) {
index 90bef91..566dcc7 100644 (file)
@@ -1517,7 +1517,7 @@ class User {
                        $count = $wgMemc->get( $key );
                        // Already pinged?
                        if( $count ) {
-                               if( $count > $max ) {
+                               if( $count >= $max ) {
                                        wfDebug( __METHOD__ . ": tripped! $key at $count $summary\n" );
                                        if( $wgRateLimitLog ) {
                                                wfSuppressWarnings();
index 47eb596..5c03617 100644 (file)
@@ -680,7 +680,7 @@ abstract class DatabaseBase implements DatabaseType {
                $dbType = strtolower( $dbType );
                $class = 'Database' . ucfirst( $dbType );
 
-               if( in_array( $dbType, $canonicalDBTypes ) ) {
+               if( in_array( $dbType, $canonicalDBTypes ) || ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) ) {
                        return new $class(
                                isset( $p['host'] ) ? $p['host'] : false,
                                isset( $p['user'] ) ? $p['user'] : false,
@@ -689,8 +689,6 @@ abstract class DatabaseBase implements DatabaseType {
                                isset( $p['flags'] ) ? $p['flags'] : 0,
                                isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : 'get from global'
                        );
-               } elseif ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) {
-                       return new $class( $p );
                } else {
                        return null;
                }
index 6452f54..e2b38f5 100644 (file)
@@ -708,14 +708,19 @@ class DatabasePostgres extends DatabaseBase {
                # Replace reserved words with better ones
                switch( $name ) {
                        case 'user':
-                               return 'mwuser';
+                               return $this->realTableName( 'mwuser', $format );
                        case 'text':
-                               return 'pagecontent';
+                               return $this->realTableName( 'pagecontent', $format );
                        default:
-                               return parent::tableName( $name, $format );
+                               return $this->realTableName( $name, $format );
                }
        }
 
+       /* Don't cheat on installer */
+       function realTableName( $name, $format = 'quoted' ) {
+               return parent::tableName( $name, $format );
+       }
+
        /**
         * Return the next in a sequence, save the value for retrieval via insertId()
         * @return null
@@ -990,7 +995,7 @@ class DatabasePostgres extends DatabaseBase {
                if ( !$schema ) {
                        $schema = $this->getCoreSchema();
                }
-               $table = $this->tableName( $table, 'raw' );
+               $table = $this->realTableName( $table, 'raw' );
                $etable = $this->addQuotes( $table );
                $eschema = $this->addQuotes( $schema );
                $SQL = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
index 0dd5466..db0dbcc 100644 (file)
@@ -119,10 +119,19 @@ class FileRepo {
                return $this->backend;
        }
 
+       /**
+        * Get an explanatory message if this repo is read-only
+        *
+        * @return string|bool Returns false if the repo is not read-only
+        */
+       public function getReadOnlyReason() {
+               return $this->backend->getReadOnlyReason();
+       }
+
        /**
         * Prepare a single zone or list of zones for usage.
         * See initDeletedDir() for additional setup needed for the 'deleted' zone.
-        * 
+        *
         * @param $doZones Array Only do a particular zones
         * @return Status
         */
index e96f257..403fc9c 100644 (file)
@@ -66,6 +66,7 @@ abstract class FileBackendStore extends FileBackend {
         */
        final public function createInternal( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                if ( strlen( $params['content'] ) > $this->maxFileSizeInternal() ) {
                        $status = Status::newFatal( 'backend-fail-maxsize',
                                $params['dst'], $this->maxFileSizeInternal() );
@@ -73,6 +74,7 @@ abstract class FileBackendStore extends FileBackend {
                        $status = $this->doCreateInternal( $params );
                        $this->clearCache( array( $params['dst'] ) );
                }
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $status;
        }
@@ -96,12 +98,14 @@ abstract class FileBackendStore extends FileBackend {
         */
        final public function storeInternal( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                if ( filesize( $params['src'] ) > $this->maxFileSizeInternal() ) {
                        $status = Status::newFatal( 'backend-fail-store', $params['dst'] );
                } else {
                        $status = $this->doStoreInternal( $params );
                        $this->clearCache( array( $params['dst'] ) );
                }
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $status;
        }
@@ -125,8 +129,10 @@ abstract class FileBackendStore extends FileBackend {
         */
        final public function copyInternal( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $status = $this->doCopyInternal( $params );
                $this->clearCache( array( $params['dst'] ) );
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $status;
        }
@@ -149,8 +155,10 @@ abstract class FileBackendStore extends FileBackend {
         */
        final public function deleteInternal( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $status = $this->doDeleteInternal( $params );
                $this->clearCache( array( $params['src'] ) );
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $status;
        }
@@ -174,8 +182,10 @@ abstract class FileBackendStore extends FileBackend {
         */
        final public function moveInternal( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $status = $this->doMoveInternal( $params );
                $this->clearCache( array( $params['src'], $params['dst'] ) );
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $status;
        }
@@ -201,6 +211,7 @@ abstract class FileBackendStore extends FileBackend {
         */
        final public function concatenate( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $status = Status::newGood();
 
                // Try to lock the source files for the scope of this function
@@ -210,6 +221,7 @@ abstract class FileBackendStore extends FileBackend {
                        $status->merge( $this->doConcatenate( $params ) );
                }
 
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $status;
        }
@@ -276,11 +288,13 @@ abstract class FileBackendStore extends FileBackend {
         */
        final protected function doPrepare( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
 
                $status = Status::newGood();
                list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
                if ( $dir === null ) {
                        $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
+                       wfProfileOut( __METHOD__ . '-' . $this->name );
                        wfProfileOut( __METHOD__ );
                        return $status; // invalid storage path
                }
@@ -295,6 +309,7 @@ abstract class FileBackendStore extends FileBackend {
                        }
                }
 
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $status;
        }
@@ -313,11 +328,13 @@ abstract class FileBackendStore extends FileBackend {
         */
        final protected function doSecure( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $status = Status::newGood();
 
                list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
                if ( $dir === null ) {
                        $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
+                       wfProfileOut( __METHOD__ . '-' . $this->name );
                        wfProfileOut( __METHOD__ );
                        return $status; // invalid storage path
                }
@@ -332,6 +349,7 @@ abstract class FileBackendStore extends FileBackend {
                        }
                }
 
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $status;
        }
@@ -350,11 +368,13 @@ abstract class FileBackendStore extends FileBackend {
         */
        final protected function doClean( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $status = Status::newGood();
 
                list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
                if ( $dir === null ) {
                        $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
+                       wfProfileOut( __METHOD__ . '-' . $this->name );
                        wfProfileOut( __METHOD__ );
                        return $status; // invalid storage path
                }
@@ -363,6 +383,7 @@ abstract class FileBackendStore extends FileBackend {
                $filesLockEx = array( $params['dir'] );
                $scopedLockE = $this->getScopedFileLocks( $filesLockEx, LockManager::LOCK_EX, $status );
                if ( !$status->isOK() ) {
+                       wfProfileOut( __METHOD__ . '-' . $this->name );
                        wfProfileOut( __METHOD__ );
                        return $status; // abort
                }
@@ -377,6 +398,7 @@ abstract class FileBackendStore extends FileBackend {
                        }
                }
 
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $status;
        }
@@ -395,7 +417,9 @@ abstract class FileBackendStore extends FileBackend {
         */
        final public function fileExists( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $stat = $this->getFileStat( $params );
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return ( $stat === null ) ? null : (bool)$stat; // null => failure
        }
@@ -406,7 +430,9 @@ abstract class FileBackendStore extends FileBackend {
         */
        final public function getFileTimestamp( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $stat = $this->getFileStat( $params );
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $stat ? $stat['mtime'] : false;
        }
@@ -417,7 +443,9 @@ abstract class FileBackendStore extends FileBackend {
         */
        final public function getFileSize( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $stat = $this->getFileStat( $params );
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $stat ? $stat['size'] : false;
        }
@@ -428,8 +456,10 @@ abstract class FileBackendStore extends FileBackend {
         */
        final public function getFileStat( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $path = self::normalizeStoragePath( $params['src'] );
                if ( $path === null ) {
+                       wfProfileOut( __METHOD__ . '-' . $this->name );
                        wfProfileOut( __METHOD__ );
                        return false; // invalid storage path
                }
@@ -438,18 +468,22 @@ abstract class FileBackendStore extends FileBackend {
                        // If we want the latest data, check that this cached
                        // value was in fact fetched with the latest available data.
                        if ( !$latest || $this->cache[$path]['stat']['latest'] ) {
+                               wfProfileOut( __METHOD__ . '-' . $this->name );
                                wfProfileOut( __METHOD__ );
                                return $this->cache[$path]['stat'];
                        }
                }
                wfProfileIn( __METHOD__ . '-miss' );
+               wfProfileIn( __METHOD__ . '-miss-' . $this->name );
                $stat = $this->doGetFileStat( $params );
+               wfProfileOut( __METHOD__ . '-miss-' . $this->name );
                wfProfileOut( __METHOD__ . '-miss' );
                if ( is_array( $stat ) ) { // don't cache negatives
                        $this->trimCache(); // limit memory
                        $this->cache[$path]['stat'] = $stat;
                        $this->cache[$path]['stat']['latest'] = $latest;
                }
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $stat;
        }
@@ -465,14 +499,17 @@ abstract class FileBackendStore extends FileBackend {
         */
        public function getFileContents( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $tmpFile = $this->getLocalReference( $params );
                if ( !$tmpFile ) {
+                       wfProfileOut( __METHOD__ . '-' . $this->name );
                        wfProfileOut( __METHOD__ );
                        return false;
                }
                wfSuppressWarnings();
                $data = file_get_contents( $tmpFile->getPath() );
                wfRestoreWarnings();
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $data;
        }
@@ -483,18 +520,23 @@ abstract class FileBackendStore extends FileBackend {
         */
        final public function getFileSha1Base36( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $path = $params['src'];
                if ( isset( $this->cache[$path]['sha1'] ) ) {
+                       wfProfileOut( __METHOD__ . '-' . $this->name );
                        wfProfileOut( __METHOD__ );
                        return $this->cache[$path]['sha1'];
                }
                wfProfileIn( __METHOD__ . '-miss' );
+               wfProfileIn( __METHOD__ . '-miss-' . $this->name );
                $hash = $this->doGetFileSha1Base36( $params );
+               wfProfileOut( __METHOD__ . '-miss-' . $this->name );
                wfProfileOut( __METHOD__ . '-miss' );
                if ( $hash ) { // don't cache negatives
                        $this->trimCache(); // limit memory
                        $this->cache[$path]['sha1'] = $hash;
                }
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $hash;
        }
@@ -518,8 +560,10 @@ abstract class FileBackendStore extends FileBackend {
         */
        final public function getFileProps( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $fsFile = $this->getLocalReference( $params );
                $props = $fsFile ? $fsFile->getProps() : FSFile::placeholderProps();
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $props;
        }
@@ -530,8 +574,10 @@ abstract class FileBackendStore extends FileBackend {
         */
        public function getLocalReference( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $path = $params['src'];
                if ( isset( $this->expensiveCache[$path]['localRef'] ) ) {
+                       wfProfileOut( __METHOD__ . '-' . $this->name );
                        wfProfileOut( __METHOD__ );
                        return $this->expensiveCache[$path]['localRef'];
                }
@@ -540,6 +586,7 @@ abstract class FileBackendStore extends FileBackend {
                        $this->trimExpensiveCache(); // limit memory
                        $this->expensiveCache[$path]['localRef'] = $tmpFile;
                }
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $tmpFile;
        }
@@ -550,6 +597,7 @@ abstract class FileBackendStore extends FileBackend {
         */
        final public function streamFile( array $params ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $status = Status::newGood();
 
                $info = $this->getFileStat( $params );
@@ -564,12 +612,15 @@ abstract class FileBackendStore extends FileBackend {
                        // do nothing; client cache is up to date
                } elseif ( $res == StreamFile::READY_STREAM ) {
                        wfProfileIn( __METHOD__ . '-send' );
+                       wfProfileIn( __METHOD__ . '-send-' . $this->name );
                        $status = $this->doStreamFile( $params );
+                       wfProfileOut( __METHOD__ . '-send-' . $this->name );
                        wfProfileOut( __METHOD__ . '-send' );
                } else {
                        $status->fatal( 'backend-fail-stream', $params['src'] );
                }
 
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $status;
        }
@@ -678,6 +729,7 @@ abstract class FileBackendStore extends FileBackend {
         */
        protected function doOperationsInternal( array $ops, array $opts ) {
                wfProfileIn( __METHOD__ );
+               wfProfileIn( __METHOD__ . '-' . $this->name );
                $status = Status::newGood();
 
                // Build up a list of FileOps...
@@ -699,6 +751,7 @@ abstract class FileBackendStore extends FileBackend {
                        $scopeLockS = $this->getScopedFileLocks( $filesLockSh, LockManager::LOCK_UW, $status );
                        $scopeLockE = $this->getScopedFileLocks( $filesLockEx, LockManager::LOCK_EX, $status );
                        if ( !$status->isOK() ) {
+                               wfProfileOut( __METHOD__ . '-' . $this->name );
                                wfProfileOut( __METHOD__ );
                                return $status; // abort
                        }
@@ -714,6 +767,7 @@ abstract class FileBackendStore extends FileBackend {
                $status->merge( $subStatus );
                $status->success = $subStatus->success; // not done in merge()
 
+               wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $status;
        }
index cd26170..7be554d 100644 (file)
@@ -908,9 +908,13 @@ class LocalFile extends File {
         */
        function upload( $srcPath, $comment, $pageText, $flags = 0, $props = false, $timestamp = false, $user = null ) {
                global $wgContLang;
+
+               if ( $this->getRepo()->getReadOnlyReason() !== false ) {
+                       return $this->readOnlyFatalStatus();
+               }
+
                // truncate nicely or the DB will do it for us
-               // non-nicely (dangling multi-byte chars, non-truncated
-               // version in cache).
+               // non-nicely (dangling multi-byte chars, non-truncated version in cache).
                $comment = $wgContLang->truncate( $comment, 255 );
                $this->lock(); // begin
                $status = $this->publish( $srcPath, $flags );
@@ -1175,6 +1179,10 @@ class LocalFile extends File {
         *     archive name, or an empty string if it was a new file.
         */
        function publishTo( $srcPath, $dstRel, $flags = 0 ) {
+               if ( $this->getRepo()->getReadOnlyReason() !== false ) {
+                       return $this->readOnlyFatalStatus();
+               }
+
                $this->lock(); // begin
 
                $archiveName = wfTimestamp( TS_MW ) . '!'. $this->getName();
@@ -1211,6 +1219,10 @@ class LocalFile extends File {
         * @return FileRepoStatus object.
         */
        function move( $target ) {
+               if ( $this->getRepo()->getReadOnlyReason() !== false ) {
+                       return $this->readOnlyFatalStatus();
+               }
+
                wfDebugLog( 'imagemove', "Got request to move {$this->name} to " . $target->getText() );
                $this->lock(); // begin
 
@@ -1250,6 +1262,10 @@ class LocalFile extends File {
         * @return FileRepoStatus object.
         */
        function delete( $reason, $suppress = false ) {
+               if ( $this->getRepo()->getReadOnlyReason() !== false ) {
+                       return $this->readOnlyFatalStatus();
+               }
+
                $this->lock(); // begin
 
                $batch = new LocalFileDeleteBatch( $this, $reason, $suppress );
@@ -1266,7 +1282,7 @@ class LocalFile extends File {
                }
                $status = $batch->execute();
 
-               if ( $status->ok ) {
+               if ( $status->isOK() ) {
                        // Update site_stats
                        $site_stats = $dbw->tableName( 'site_stats' );
                        $dbw->query( "UPDATE $site_stats SET ss_images=ss_images-1", __METHOD__ );
@@ -1293,6 +1309,10 @@ class LocalFile extends File {
         * @return FileRepoStatus object.
         */
        function deleteOld( $archiveName, $reason, $suppress = false ) {
+               if ( $this->getRepo()->getReadOnlyReason() !== false ) {
+                       return $this->readOnlyFatalStatus();
+               }
+
                $this->lock(); // begin
 
                $batch = new LocalFileDeleteBatch( $this, $reason, $suppress );
@@ -1302,7 +1322,7 @@ class LocalFile extends File {
 
                $this->unlock(); // done
 
-               if ( $status->ok ) {
+               if ( $status->isOK() ) {
                        $this->purgeDescription();
                        $this->purgeHistory();
                }
@@ -1322,6 +1342,12 @@ class LocalFile extends File {
         * @return FileRepoStatus
         */
        function restore( $versions = array(), $unsuppress = false ) {
+               if ( $this->getRepo()->getReadOnlyReason() !== false ) {
+                       return $this->readOnlyFatalStatus();
+               }
+
+               $this->lock(); // begin
+
                $batch = new LocalFileRestoreBatch( $this, $unsuppress );
 
                if ( !$versions ) {
@@ -1332,14 +1358,14 @@ class LocalFile extends File {
 
                $status = $batch->execute();
 
-               if ( !$status->isGood() ) {
-                       return $status;
+               if ( $status->isGood() ) {
+                       $cleanupStatus = $batch->cleanup();
+                       $cleanupStatus->successCount = 0;
+                       $cleanupStatus->failCount = 0;
+                       $status->merge( $cleanupStatus );
                }
 
-               $cleanupStatus = $batch->cleanup();
-               $cleanupStatus->successCount = 0;
-               $cleanupStatus->failCount = 0;
-               $status->merge( $cleanupStatus );
+               $this->unlock(); // done
 
                return $status;
        }
@@ -1444,6 +1470,14 @@ class LocalFile extends File {
                $dbw = $this->repo->getMasterDB();
                $dbw->rollback( __METHOD__ );
        }
+
+       /**
+        * @return Status
+        */
+       protected function readOnlyFatalStatus() {
+               return $this->getRepo()->newFatal( 'filereadonlyerror', $this->getName(),
+                       $this->getRepo()->getName(), $this->getRepo()->getReadOnlyReason() );
+       }
 } // LocalFile class
 
 # ------------------------------------------------------------------------------
@@ -1711,7 +1745,7 @@ class LocalFileDeleteBatch {
                        $this->status->merge( $status );
                }
 
-               if ( !$this->status->ok ) {
+               if ( !$this->status->isOK() ) {
                        // Critical file deletion error
                        // Roll back inserts, release lock and abort
                        // TODO: delete the defunct filearchive rows if we are using a non-transactional DB
index 14604c1..046fa16 100644 (file)
@@ -158,7 +158,7 @@ abstract class DatabaseInstaller {
                }
                $this->db->selectDB( $this->getVar( 'wgDBname' ) );
 
-               if( $this->db->tableExists( 'user', __METHOD__ ) ) {
+               if( $this->db->tableExists( 'archive', __METHOD__ ) ) {
                        $status->warning( 'config-install-tables-exist' );
                        $this->enableLB();
                        return $status;
index d4412cb..6b3cb51 100644 (file)
@@ -27,6 +27,11 @@ class PostgresUpdater extends DatabaseUpdater {
         */
        protected function getCoreUpdateList() {
                return array(
+                       # rename tables 1.7.3
+                       # r15791 Change reserved word table names "user" and "text"
+                       array( 'renameTable', 'user', 'mwuser'),
+                       array( 'renameTable', 'text', 'pagecontent'),
+
                        # new sequences
                        array( 'addSequence', 'logging_log_id_seq'          ),
                        array( 'addSequence', 'page_restrictions_pr_id_seq' ),
@@ -406,7 +411,8 @@ END;
        protected function renameTable( $old, $new ) {
                if ( $this->db->tableExists( $old ) ) {
                        $this->output( "Renaming table $old to $new\n" );
-                       $old = $this->db->addQuotes( $old );
+                       $old = $this->db->realTableName( $old, "quoted" );
+                       $new = $this->db->realTableName( $new, "quoted" );
                        $this->db->query( "ALTER TABLE $old RENAME TO $new" );
                }
        }
index f8208f6..4f49f7d 100644 (file)
@@ -344,11 +344,20 @@ class MWMemcached {
                return false;
        }
 
+       /**
+        * @param $key
+        * @param $timeout int
+        * @return bool
+        */
        public function lock( $key, $timeout = 0 ) {
                /* stub */
                return true;
        }
 
+       /**
+        * @param $key
+        * @return bool
+        */
        public function unlock( $key ) {
                /* stub */
                return true;
@@ -471,7 +480,7 @@ class MWMemcached {
                        $this->stats['get_multi'] = 1;
                }
                $sock_keys = array();
-
+               $socks = array();
                foreach ( $keys as $key ) {
                        $sock = $this->get_sock( $key );
                        if ( !is_resource( $sock ) ) {
@@ -485,6 +494,7 @@ class MWMemcached {
                        $sock_keys[$sock][] = $key;
                }
 
+               $gather = array();
                // Send out the requests
                foreach ( $socks as $sock ) {
                        $cmd = 'get';
@@ -579,6 +589,7 @@ class MWMemcached {
                        return array();
                }
 
+               $ret = array();
                while ( true ) {
                        $res = fgets( $sock );
                        $ret[] = $res;
@@ -744,6 +755,9 @@ class MWMemcached {
                $this->_dead_host( $host );
        }
 
+       /**
+        * @param $host
+        */
        function _dead_host( $host ) {
                $parts = explode( ':', $host );
                $ip = $parts[0];
@@ -774,8 +788,8 @@ class MWMemcached {
                }
 
                $hv = is_array( $key ) ? intval( $key[0] ) : $this->_hashfunc( $key );
-
                if ( $this->_buckets === null ) {
+                       $bu = array();
                        foreach ( $this->_servers as $v ) {
                                if ( is_array( $v ) ) {
                                        for( $i = 0; $i < $v[1]; $i++ ) {
@@ -851,7 +865,8 @@ class MWMemcached {
                        $this->stats[$cmd] = 1;
                }
                if ( !$this->_safe_fwrite( $sock, "$cmd $key $amt\r\n" ) ) {
-                       return $this->_dead_sock( $sock );
+                       $this->_dead_sock( $sock );
+                       return null;
                }
 
                $line = fgets( $sock );
@@ -998,7 +1013,8 @@ class MWMemcached {
                        }
                }
                if ( !$this->_safe_fwrite( $sock, "$cmd $key $flags $exp $len\r\n$val\r\n" ) ) {
-                       return $this->_dead_sock( $sock );
+                       $this->_dead_sock( $sock );
+                       return false;
                }
 
                $line = trim( fgets( $sock ) );
@@ -1038,7 +1054,8 @@ class MWMemcached {
                }
 
                if ( !$this->_connect_sock( $sock, $host ) ) {
-                       return $this->_dead_host( $host );
+                       $this->_dead_host( $host );
+                       return null;
                }
 
                // Do not buffer writes
@@ -1049,6 +1066,9 @@ class MWMemcached {
                return $this->_cache_sock[$host];
        }
 
+       /**
+        * @param $str string
+        */
        function _debugprint( $str ) {
                print( $str );
        }
@@ -1080,6 +1100,9 @@ class MWMemcached {
 
        /**
         * Original behaviour
+        * @param $f
+        * @param $buf
+        * @param $len bool
         * @return int
         */
        function _safe_fwrite( $f, $buf, $len = false ) {
@@ -1093,6 +1116,7 @@ class MWMemcached {
 
        /**
         * Flush the read buffer of a stream
+        * @param $f Resource
         */
        function _flush_read_buffer( $f ) {
                if ( !is_resource( $f ) ) {
index b1ed9b6..f6d8b3a 100644 (file)
@@ -48,14 +48,7 @@ class Profiler {
                        $this->mProfileID = $params['profileID'];
                }
 
-               // Push an entry for the pre-profile setup time onto the stack
-               $initial = $this->getInitialTime();
-               if ( $initial !== null ) {
-                       $this->mWorkStack[] = array( '-total', 0, $initial, 0 );
-                       $this->mStack[] = array( '-setup', 1, $initial, 0, $this->getTime(), 0 );
-               } else {
-                       $this->profileIn( '-total' );
-               }
+               $this->addInitialStack();
        }
 
        /**
@@ -114,6 +107,20 @@ class Profiler {
                }
        }
 
+       /**
+        * Add the inital item in the stack.
+        */
+       protected function addInitialStack() {
+               // Push an entry for the pre-profile setup time onto the stack
+               $initial = $this->getInitialTime();
+               if ( $initial !== null ) {
+                       $this->mWorkStack[] = array( '-total', 0, $initial, 0 );
+                       $this->mStack[] = array( '-setup', 1, $initial, 0, $this->getTime(), 0 );
+               } else {
+                       $this->profileIn( '-total' );
+               }
+       }
+
        /**
         * Called by wfProfieIn()
         *
index 055a0ea..df37363 100644 (file)
@@ -15,32 +15,20 @@ class ProfilerSimple extends Profiler {
        var $zeroEntry = array('cpu'=> 0.0, 'cpu_sq' => 0.0, 'real' => 0.0, 'real_sq' => 0.0, 'count' => 0);
        var $errorEntry;
 
-       function __construct( $params ) {
+       protected function addInitialStack() {
                global $wgRequestTime, $wgRUstart;
-               parent::__construct( $params );
 
                $this->errorEntry = $this->zeroEntry;
                $this->errorEntry['count'] = 1;
 
-               if (!empty($wgRequestTime) && !empty($wgRUstart)) {
-                       # Remove the -total entry from parent::__construct
-                       $this->mWorkStack = array();
+               if ( !empty( $wgRequestTime ) && !empty( $wgRUstart ) ) {
+                       $initialCpu = $this->getCpuTime( $wgRUstart );
+                       $this->mWorkStack[] = array( '-total', 0, $wgRequestTime, $initialCpu );
+                       $this->mWorkStack[] = array( '-setup', 1, $wgRequestTime, $initialCpu );
 
-                       $this->mWorkStack[] = array( '-total', 0, $wgRequestTime,$this->getCpuTime($wgRUstart));
-
-                       $elapsedcpu = $this->getCpuTime() - $this->getCpuTime($wgRUstart);
-                       $elapsedreal = microtime(true) - $wgRequestTime;
-
-                       $entry =& $this->mCollated["-setup"];
-                       if (!is_array($entry)) {
-                               $entry = $this->zeroEntry;
-                               $this->mCollated["-setup"] =& $entry;
-                       }
-                       $entry['cpu'] += $elapsedcpu;
-                       $entry['cpu_sq'] += $elapsedcpu*$elapsedcpu;
-                       $entry['real'] += $elapsedreal;
-                       $entry['real_sq'] += $elapsedreal*$elapsedreal;
-                       $entry['count']++;
+                       $this->profileOut( '-setup' );
+               } else {
+                       $this->profileIn( '-total' );
                }
        }
 
index 5b5b356..a58e62e 100644 (file)
@@ -381,19 +381,19 @@ class SpecialBlock extends FormSpecialPage {
                        $this->getLanguage()->pipeList( $links )
                );
 
-               if( $this->target instanceof User ){
+               $userTitle = self::getTargetUserTitle( $this->target );
+               if( $userTitle ){
                        # Get relevant extracts from the block and suppression logs, if possible
-                       $userpage = $this->target->getUserPage();
                        $out = '';
 
                        LogEventsList::showLogExtract(
                                $out,
                                'block',
-                               $userpage,
+                               $userTitle,
                                '',
                                array(
                                        'lim' => 10,
-                                       'msgKey' => array( 'blocklog-showlog', $userpage->getText() ),
+                                       'msgKey' => array( 'blocklog-showlog', $userTitle->getText() ),
                                        'showIfEmpty' => false
                                )
                        );
@@ -404,12 +404,12 @@ class SpecialBlock extends FormSpecialPage {
                                LogEventsList::showLogExtract(
                                        $out,
                                        'suppress',
-                                       $userpage,
+                                       $userTitle,
                                        '',
                                        array(
                                                'lim' => 10,
                                                'conds' => array( 'log_action' => array( 'block', 'reblock', 'unblock' ) ),
-                                               'msgKey' => array( 'blocklog-showsuppresslog', $userpage->getText() ),
+                                               'msgKey' => array( 'blocklog-showsuppresslog', $userTitle->getText() ),
                                                'showIfEmpty' => false
                                        )
                                );
@@ -421,6 +421,21 @@ class SpecialBlock extends FormSpecialPage {
                return $text;
        }
 
+       /**
+        * Get a user page target for things like logs.
+        * This handles account and IP range targets.
+        * @param $target User|string
+        * @return Title|null
+        */
+       protected static function getTargetUserTitle( $target ) {
+               if( $target instanceof User ) {
+                       return $target->getUserPage();
+               } elseif ( IP::isIPAddress( $target ) ) {
+                       return Title::makeTitleSafe( NS_USER, $target );
+               }
+               return null;
+       }
+
        /**
         * Determine the target of the block, and the type of target
         * TODO: should be in Block.php?
index b88123d..45dbd36 100644 (file)
@@ -123,7 +123,7 @@ class NewFilesPager extends ReverseChronologicalPager {
                $this->gallery->add(
                        $title,
                        "$ul<br />\n<i>"
-                               . htmlspecialchars( $this->getLanguage()->timeanddate( $row->img_timestamp, true ) )
+                               . htmlspecialchars( $this->getLanguage()->userTimeAndDate( $row->img_timestamp, $this->getUser() ) )
                                . "</i><br />\n"
                );
        }
@@ -139,7 +139,7 @@ class NewFilesPager extends ReverseChronologicalPager {
                        ),
                        'showbots' => array(
                                'type' => 'check',
-                               'label' => wfMessage( 'showhidebots', wfMsg( 'show' ) ),
+                               'label' => $this->msg( 'showhidebots', $this->msg( 'show' )->plain() )->escaped(),
                                'name' => 'showbots',
                        #       'default' => $this->getRequest()->getBool( 'showbots', 0 ),
                        ),
@@ -161,9 +161,9 @@ class NewFilesPager extends ReverseChronologicalPager {
 
                $form = new HTMLForm( $fields, $this->getContext() );
                $form->setTitle( $this->getTitle() );
-               $form->setSubmitText( wfMsg( 'ilsubmit' ) );
+               $form->setSubmitTextMsg( 'ilsubmit' );
                $form->setMethod( 'get' );
-               $form->setWrapperLegend( wfMsg( 'newimages-legend' ) );
+               $form->setWrapperLegendMsg( 'newimages-legend' );
 
                return $form;
        }
index 5e58841..683a714 100644 (file)
@@ -98,7 +98,7 @@ class SpecialPasswordReset extends FormSpecialPage {
        }
 
        public function alterForm( HTMLForm $form ) {
-               $form->setSubmitText( wfMessage( "mailmypassword" ) );
+               $form->setSubmitTextMsg( 'mailmypassword' );
        }
 
        protected function preText() {
@@ -113,7 +113,7 @@ class SpecialPasswordReset extends FormSpecialPage {
                if ( isset( $wgPasswordResetRoutes['domain'] ) && $wgPasswordResetRoutes['domain'] ) {
                        $i++;
                }
-               return wfMessage( 'passwordreset-pretext', $i )->parseAsBlock();
+               return $this->msg( 'passwordreset-pretext', $i )->parseAsBlock();
        }
 
        /**
@@ -234,14 +234,14 @@ class SpecialPasswordReset extends FormSpecialPage {
                        $password = $user->randomPassword();
                        $user->setNewpassword( $password );
                        $user->saveSettings();
-                       $passwords[] = wfMessage( 'passwordreset-emailelement', $user->getName(), $password )->plain(); // We'll escape the whole thing later
+                       $passwords[] = $this->msg( 'passwordreset-emailelement', $user->getName(), $password )->plain(); // We'll escape the whole thing later
                }
                $passwordBlock = implode( "\n\n", $passwords );
 
                // Send in the user's language; which should hopefully be the same
                $userLanguage = $firstUser->getOption( 'language' );
 
-               $this->email = wfMessage( $msg )->inLanguage( $userLanguage );
+               $this->email = $this->msg( $msg )->inLanguage( $userLanguage );
                $this->email->params(
                        $username,
                        $passwordBlock,
@@ -250,7 +250,7 @@ class SpecialPasswordReset extends FormSpecialPage {
                        round( $wgNewPasswordExpiry / 86400 )
                );
 
-               $title = wfMessage( 'passwordreset-emailtitle' );
+               $title = $this->msg( 'passwordreset-emailtitle' );
 
                $this->result = $firstUser->sendMail( $title->escaped(), $this->email->escaped() );
 
index bfc5248..c05aaad 100644 (file)
@@ -568,14 +568,14 @@ class SpecialRecentChanges extends IncludableSpecialPage {
                $submit = ' ' . Xml::submitbutton( wfMsg( 'allpagessubmit' ) );
 
                $out = Xml::openElement( 'table', array( 'class' => 'mw-recentchanges-table' ) );
-               foreach( $extraOpts as $optionRow ) {
+               foreach( $extraOpts as $name => $optionRow ) {
                        # Add submit button to the last row only
                        ++$count;
-                       $addSubmit = $count === $extraOptsCount ? $submit : '';
+                       $addSubmit = ( $count === $extraOptsCount ) ? $submit : '';
 
                        $out .= Xml::openElement( 'tr' );
                        if( is_array( $optionRow ) ) {
-                               $out .= Xml::tags( 'td', array( 'class' => 'mw-label' ), $optionRow[0] );
+                               $out .= Xml::tags( 'td', array( 'class' => 'mw-label mw-' . $name . '-label' ), $optionRow[0] );
                                $out .= Xml::tags( 'td', array( 'class' => 'mw-input' ), $optionRow[1] . $addSubmit );
                        } else {
                                $out .= Xml::tags( 'td', array( 'class' => 'mw-input', 'colspan' => 2 ), $optionRow . $addSubmit );
index 2b44337..9181de0 100644 (file)
@@ -160,10 +160,14 @@ class SpecialVersion extends SpecialPage {
                global $wgVersion, $IP;
                wfProfileIn( __METHOD__ );
 
-               $info = self::getSvnInfo( $IP );
-               if ( !$info ) {
+               $gitInfo = self::getGitHeadSha1( $IP );
+               $svnInfo = self::getSvnInfo( $IP );
+               if ( !$svnInfo && !$gitInfo ) {
                        $version = $wgVersion;
-               } elseif( $flags === 'nodb' ) {
+               } elseif ( $gitInfo ) {
+                       $shortSha1 = substr( $gitInfo, 0, 7 );
+                       $version = "$wgVersion ($shortSha1)";
+               } elseif ( $flags === 'nodb' ) {
                        $version = "$wgVersion (r{$info['checkout-rev']})";
                } else {
                        $version = $wgVersion . ' ' .
@@ -717,24 +721,8 @@ class SpecialVersion extends SpecialPage {
         * @return bool|String sha1 of commit HEAD points to
         */
        public static function getGitHeadSha1( $dir ) {
-               $BASEDIR  = "{$dir}/.git/";
-               $HEADfile = "{$BASEDIR}/HEAD";
-
-               if( !file_exists( $HEADfile ) ) {
-                       return false;
-               }
-
-               preg_match( "/ref: (.*)/",
-                       file_get_contents( $HEADfile ), $m );
-
-               $REFfile = "{$BASEDIR}{$m[1]}";
-               if( !file_exists( $REFfile ) ) {
-                       return false;
-               }
-
-               $sha1 = rtrim( file_get_contents( $REFfile ) );
-
-               return $sha1;
+               $repo = new GitInfo( $dir );
+               return $repo->getHeadSHA1();
        }
 
        function showEasterEgg() {
index 64a07f1..0c5f11c 100644 (file)
@@ -40,22 +40,10 @@ class SpecialWatchlist extends SpecialPage {
                $user = $this->getUser();
                $output = $this->getOutput();
 
-               // Add feed links
-               $wlToken = $user->getOption( 'watchlisttoken' );
-               if ( !$wlToken ) {
-                       $wlToken = MWCryptRand::generateHex( 40 );
-                       $user->setOption( 'watchlisttoken', $wlToken );
-                       $user->saveSettings();
-               }
-
-               $this->addFeedLinks( array( 'action' => 'feedwatchlist', 'allrev' => 'allrev',
-                                                       'wlowner' => $user->getName(), 'wltoken' => $wlToken ) );
-
-               $output->setRobotPolicy( 'noindex,nofollow' );
-
                # Anons don't get a watchlist
                if( $user->isAnon() ) {
                        $output->setPageTitle( $this->msg( 'watchnologin' ) );
+                       $output->setRobotPolicy( 'noindex,nofollow' );
                        $llink = Linker::linkKnown(
                                SpecialPage::getTitleFor( 'Userlogin' ),
                                $this->msg( 'loginreqlink' )->escaped(),
@@ -66,6 +54,17 @@ class SpecialWatchlist extends SpecialPage {
                        return;
                }
 
+               // Add feed links
+               $wlToken = $user->getOption( 'watchlisttoken' );
+               if ( !$wlToken ) {
+                       $wlToken = MWCryptRand::generateHex( 40 );
+                       $user->setOption( 'watchlisttoken', $wlToken );
+                       $user->saveSettings();
+               }
+
+               $this->addFeedLinks( array( 'action' => 'feedwatchlist', 'allrev' => 'allrev',
+                                                       'wlowner' => $user->getName(), 'wltoken' => $wlToken ) );
+
                $this->setHeaders();
                $this->outputHeader();
 
index 854a9f1..d705b49 100644 (file)
@@ -3758,7 +3758,7 @@ class Language {
 
        /**
         * Decode an expiry (block, protection, etc) which has come from the DB
-        * 
+        *
         * @FIXME: why are we returnings DBMS-dependent strings???
         *
         * @param $expiry String: Database expiry String
index a40fb7a..22e8946 100644 (file)
@@ -41,11 +41,11 @@ class LanguageKaa extends Language {
        }
 
        /**
-        * It fixes issue with  lcfirst for transforming 'I' to 'ı'
+        * It fixes issue with lcfirst for transforming 'I' to 'ı'
         *
         * @param $string string
         *
-        * @return string
+        * @return mixed|string
         */
        function lcfirst ( $string ) {
                if ( substr( $string, 0, 1 ) === 'I' ) {
index 933cff1..ee8a2ed 100644 (file)
@@ -997,6 +997,9 @@ Please report this to an [[Special:ListUsers/sysop|administrator]], making note
 'directorycreateerror' => 'Could not create directory "$1".',
 'filenotfound'         => 'Could not find file "$1".',
 'fileexistserror'      => 'Unable to write to file "$1": File exists.',
+'filereadonlyerror'    => 'Unable to the modify the file "$1" because the file repository "$2" is in read-only mode.
+
+The administrator who locked it offered this explanation: "$3".',
 'unexpected'           => 'Unexpected value: "$1"="$2".',
 'formerror'            => 'Error: Could not submit form.',
 'badarticleerror'      => 'This action cannot be performed on this page.',
index 989f7bd..4f5889b 100644 (file)
@@ -74,7 +74,7 @@ class DeleteDefaultMessages extends Maintenance {
                        $dbw->commit( __METHOD__ );
                }
 
-               $this->output( 'done!\n', 'msg' );
+               $this->output( "done!\n", 'msg' );
        }
 }
 
index c03f3df..6796c75 100644 (file)
@@ -41,9 +41,7 @@ class TextPassDumper extends BackupDumper {
        var $prefetchCountLast = 0;
        var $fetchCountLast = 0;
 
-       var $failures = 0;
        var $maxFailures = 5;
-       var $failedTextRetrievals = 0;
        var $maxConsecutiveFailedTextRetrievals = 200;
        var $failureTimeout = 5; // Seconds to sleep after db failure
 
@@ -71,6 +69,52 @@ class TextPassDumper extends BackupDumper {
         */
        protected $db;
 
+
+       /**
+        * Drop the database connection $this->db and try to get a new one.
+        *
+        * This function tries to get a /different/ connection if this is
+        * possible. Hence, (if this is possible) it switches to a different
+        * failover upon each call.
+        *
+        * This function resets $this->lb and closes all connections on it.
+        *
+        * @throws MWException
+        */
+       function rotateDb() {
+               // Cleaning up old connections
+               if ( isset( $this->lb ) ) {
+                       $this->lb->closeAll();
+                       unset( $this->lb );
+               }
+
+               if ( isset( $this->db ) && $this->db->isOpen() ) {
+                       throw new MWException( 'DB is set and has not been closed by the Load Balancer' );
+               }
+
+               unset( $this->db );
+
+               // Trying to set up new connection.
+               // We do /not/ retry upon failure, but delegate to encapsulating logic, to avoid
+               // individually retrying at different layers of code.
+
+               // 1. The LoadBalancer.
+               try {
+                       $this->lb = wfGetLBFactory()->newMainLB();
+               } catch ( Exception $e ) {
+                       throw new MWException( __METHOD__ . " rotating DB failed to obtain new load balancer (" . $e->getMessage() . ")" );
+               }
+
+
+               // 2. The Connection, through the load balancer.
+               try {
+                       $this->db = $this->lb->getConnection( DB_SLAVE, 'backup' );
+               } catch ( Exception $e ) {
+                       throw new MWException( __METHOD__ . " rotating DB failed to obtain new database (" . $e->getMessage() . ")" );
+               }
+       }
+
+
        function initProgress( $history ) {
                parent::initProgress();
                $this->timeOfCheckpoint = $this->startTime;
@@ -87,7 +131,19 @@ class TextPassDumper extends BackupDumper {
 
                $this->initProgress( $this->history );
 
-               $this->db = $this->backupDb();
+               // We are trying to get an initial database connection to avoid that the
+               // first try of this request's first call to getText fails. However, if
+               // obtaining a good DB connection fails it's not a serious issue, as
+               // getText does retry upon failure and can start without having a working
+               // DB connection.
+               try {
+                       $this->rotateDb();
+               } catch ( Exception $e ) {
+                       // We do not even count this as failure. Just let eventual
+                       // watchdogs know.
+                       $this->progress( "Getting initial DB connection failed (" .
+                               $e->getMessage() . ")" );
+               }
 
                $this->egress = new ExportProgressFilter( $this->sink, $this );
 
@@ -124,7 +180,7 @@ class TextPassDumper extends BackupDumper {
                        $this->input = $url;
                        break;
                case 'maxtime':
-                       $this->maxTimeAllowed = intval($val)*60;
+                       $this->maxTimeAllowed = intval( $val ) * 60;
                        break;
                case 'checkpointfile':
                        $this->checkpointFiles[] = $val;
@@ -145,7 +201,7 @@ class TextPassDumper extends BackupDumper {
        }
 
        function processFileOpt( $val, $param ) {
-               $fileURIs = explode(';',$param);
+               $fileURIs = explode( ';', $param );
                foreach ( $fileURIs as $URI ) {
                        switch( $val ) {
                                case "file":
@@ -240,19 +296,19 @@ class TextPassDumper extends BackupDumper {
        function finalOptionCheck() {
                if ( ( $this->checkpointFiles && ! $this->maxTimeAllowed ) ||
                        ( $this->maxTimeAllowed && !$this->checkpointFiles ) ) {
-                       throw new MWException("Options checkpointfile and maxtime must be specified together.\n");
+                       throw new MWException( "Options checkpointfile and maxtime must be specified together.\n" );
                }
-               foreach ($this->checkpointFiles as $checkpointFile) {
-                       $count = substr_count ( $checkpointFile,"%s" );
+               foreach ( $this->checkpointFiles as $checkpointFile ) {
+                       $count = substr_count ( $checkpointFile, "%s" );
                        if ( $count != 2 ) {
-                               throw new MWException("Option checkpointfile must contain two '%s' for substitution of first and last pageids, count is $count instead, file is $checkpointFile.\n");
+                               throw new MWException( "Option checkpointfile must contain two '%s' for substitution of first and last pageids, count is $count instead, file is $checkpointFile.\n" );
                        }
                }
 
                if ( $this->checkpointFiles ) {
                        $filenameList = (array)$this->egress->getFilenames();
                        if ( count( $filenameList ) != count( $this->checkpointFiles ) ) {
-                               throw new MWException("One checkpointfile must be specified for each output option, if maxtime is used.\n");
+                               throw new MWException( "One checkpointfile must be specified for each output option, if maxtime is used.\n" );
                        }
                }
        }
@@ -275,7 +331,7 @@ class TextPassDumper extends BackupDumper {
                $offset = 0; // for context extraction on error reporting
                $bufferSize = 512 * 1024;
                do {
-                       if ($this->checkIfTimeExceeded()) {
+                       if ( $this->checkIfTimeExceeded() ) {
                                $this->setTimeExceeded();
                        }
                        $chunk = fread( $input, $bufferSize );
@@ -285,27 +341,27 @@ class TextPassDumper extends BackupDumper {
                        }
                        $offset += strlen( $chunk );
                } while ( $chunk !== false && !feof( $input ) );
-               if ($this->maxTimeAllowed) {
+               if ( $this->maxTimeAllowed ) {
                        $filenameList = (array)$this->egress->getFilenames();
                        // we wrote some stuff after last checkpoint that needs renamed
-                       if (file_exists($filenameList[0])) {
+                       if ( file_exists( $filenameList[0] ) ) {
                                $newFilenames = array();
                                # we might have just written the header and footer and had no
                                # pages or revisions written... perhaps they were all deleted
                                # there's no pageID 0 so we use that. the caller is responsible
                                # for deciding what to do with a file containing only the
                                # siteinfo information and the mw tags.
-                               if (! $this->firstPageWritten) {
-                                       $firstPageID = str_pad(0,9,"0",STR_PAD_LEFT);
-                                       $lastPageID = str_pad(0,9,"0",STR_PAD_LEFT);
+                               if ( ! $this->firstPageWritten ) {
+                                       $firstPageID = str_pad( 0, 9, "0", STR_PAD_LEFT );
+                                       $lastPageID = str_pad( 0, 9, "0", STR_PAD_LEFT );
                                }
                                else {
-                                       $firstPageID = str_pad($this->firstPageWritten,9,"0",STR_PAD_LEFT);
-                                       $lastPageID = str_pad($this->lastPageWritten,9,"0",STR_PAD_LEFT);
+                                       $firstPageID = str_pad( $this->firstPageWritten, 9, "0", STR_PAD_LEFT );
+                                       $lastPageID = str_pad( $this->lastPageWritten, 9, "0", STR_PAD_LEFT );
                                }
                                for ( $i = 0; $i < count( $filenameList ); $i++ ) {
                                        $checkpointNameFilledIn = sprintf( $this->checkpointFiles[$i], $firstPageID, $lastPageID );
-                                       $fileinfo = pathinfo($filenameList[$i]);
+                                       $fileinfo = pathinfo( $filenameList[$i] );
                                        $newFilenames[] = $fileinfo['dirname'] . '/' . $checkpointNameFilledIn;
                                }
                                $this->egress->closeAndRename( $newFilenames );
@@ -316,98 +372,142 @@ class TextPassDumper extends BackupDumper {
                return true;
        }
 
+       /**
+        * Tries to get the revision text for a revision id.
+        *
+        * Upon errors, retries (Up to $this->maxFailures tries each call).
+        * If still no good revision get could be found even after this retrying, "" is returned.
+        * If no good revision text could be returned for
+        * $this->maxConsecutiveFailedTextRetrievals consecutive calls to getText, MWException
+        * is thrown.
+        *
+        * @param $id string The revision id to get the text for
+        *
+        * @return string The revision text for $id, or ""
+        * @throws MWException
+        */
        function getText( $id ) {
+               $prefetchNotTried = true; // Whether or not we already tried to get the text via prefetch.
+               $text = false; // The candidate for a good text. false if no proper value.
+               $failures = 0; // The number of times, this invocation of getText already failed.
+
+               static $consecutiveFailedTextRetrievals = 0; // The number of times getText failed without
+                                                            // yielding a good text in between.
+
                $this->fetchCount++;
-               if ( isset( $this->prefetch ) ) {
-                       $text = $this->prefetch->prefetch( $this->thisPage, $this->thisRev );
-                       if ( $text !== null ) { // Entry missing from prefetch dump
-                               $dbr = wfGetDB( DB_SLAVE );
+
+               // To allow to simply return on success and do not have to worry about book keeping,
+               // we assume, this fetch works (possible after some retries). Nevertheless, we koop
+               // the old value, so we can restore it, if problems occur (See after the while loop).
+               $oldConsecutiveFailedTextRetrievals = $consecutiveFailedTextRetrievals;
+               $consecutiveFailedTextRetrievals = 0;
+
+               while ( $failures < $this->maxFailures ) {
+
+                       // As soon as we found a good text for the $id, we will return immediately.
+                       // Hence, if we make it past the try catch block, we know that we did not
+                       // find a good text.
+
+                       try {
+                               // Step 1: Get some text (or reuse from previous iteratuon if checking
+                               //         for plausibility failed)
+
+                               // Trying to get prefetch, if it has not been tried before
+                               if ( $text === false && isset( $this->prefetch ) && $prefetchNotTried ) {
+                                       $prefetchNotTried = false;
+                                       $tryIsPrefetch = true;
+                                       $text = $this->prefetch->prefetch( $this->thisPage, $this->thisRev );
+                                       if ( $text === null ) {
+                                               $text = false;
+                                       }
+                               }
+
+                               if ( $text === false ) {
+                                       // Fallback to asking the database
+                                       $tryIsPrefetch = false;
+                                       if ( $this->spawn ) {
+                                               $text = $this->getTextSpawned( $id );
+                                       } else {
+                                               $text = $this->getTextDb( $id );
+                                       }
+                               }
+
+                               if ( $text === false ) {
+                                       throw new MWException( "Generic error while obtaining text for id " . $id );
+                               }
+
+                               // We received a good candidate for the text of $id via some method
+
+                               // Step 2: Checking for plausibility and return the text if it is
+                               //         plausible
                                $revID = intval( $this->thisRev );
-                               $revLength = $dbr->selectField( 'revision', 'rev_len', array( 'rev_id' => $revID ) );
-                               // if length of rev text in file doesn't match length in db, we reload
-                               // this avoids carrying forward broken data from previous xml dumps
-                               if( strlen( $text ) == $revLength ) {
-                                       $this->prefetchCount++;
+                               if ( ! isset( $this->db ) ) {
+                                       throw new MWException( "No database available" );
+                               }
+                               $revLength = $this->db->selectField( 'revision', 'rev_len', array( 'rev_id' => $revID ) );
+                               if ( strlen( $text ) == $revLength ) {
+                                       if ( $tryIsPrefetch ) {
+                                               $this->prefetchCount++;
+                                       }
                                        return $text;
                                }
+
+                               $text = false;
+                               throw new MWException( "Received text is unplausible for id " . $id );
+
+                       } catch ( Exception $e ) {
+                               $msg = "getting/checking text " . $id . " failed (" . $e->getMessage() . ")";
+                               if ( $failures + 1 < $this->maxFailures ) {
+                                       $msg .= " (Will retry " . ( $this->maxFailures - $failures - 1 ) . " more times)";
+                               }
+                               $this->progress( $msg );
                        }
-               }
-               return $this->doGetText( $id );
-       }
 
-       private function doGetText( $id ) {
-               $id = intval( $id );
-               $this->failures = 0;
-               $ex = new MWException( "Graceful storage failure" );
-               while (true) {
-                       if ( $this->spawn ) {
-                               if ($this->failures) {
-                                       // we don't know why it failed, could be the child process
-                                       // borked, could be db entry busted, could be db server out to lunch,
-                                       // so cover all bases
+                       // Something went wrong; we did not a text that was plausible :(
+                       $failures++;
+
+
+                       // After backing off for some time, we try to reboot the whole process as
+                       // much as possible to not carry over failures from one part to the other
+                       // parts
+                       sleep( $this->failureTimeout );
+                       try {
+                               $this->rotateDb();
+                               if ( $this->spawn ) {
                                        $this->closeSpawn();
                                        $this->openSpawn();
                                }
-                               $text = $this->getTextSpawned( $id );
-                       } else {
-                               $text = $this->getTextDbSafe( $id );
-                       }
-                       if ( $text === false ) {
-                               $this->failures++;
-                               if ( $this->failures > $this->maxFailures) {
-                                       $this->progress( "Failed to retrieve revision text for text id ".
-                                                                        "$id after $this->maxFailures tries, giving up" );
-                                       // were there so many bad retrievals in a row we want to bail?
-                                       // at some point we have to declare the dump irretrievably broken
-                                       $this->failedTextRetrievals++;
-                                       if ($this->failedTextRetrievals > $this->maxConsecutiveFailedTextRetrievals) {
-                                               throw $ex;
-                                       } else {
-                                               // would be nice to return something better to the caller someday,
-                                               // log what we know about the failure and about the revision
-                                               return "";
-                                       }
-                               } else {
-                                       $this->progress( "Error $this->failures " .
-                                                                "of allowed $this->maxFailures retrieving revision text for text id $id! " .
-                                                                "Pausing $this->failureTimeout seconds before retry..." );
-                                       sleep( $this->failureTimeout );
-                               }
-                       } else {
-                               $this->failedTextRetrievals= 0;
-                               return $text;
+                       } catch ( Exception $e ) {
+                               $this->progress( "Rebooting getText infrastructure failed (" . $e->getMessage() . ")" .
+                                       " Trying to continue anyways" );
                        }
                }
-               return '';
-       }
 
-       /**
-        * Fetch a text revision from the database, retrying in case of failure.
-        * This may survive some transitory errors by reconnecting, but
-        * may not survive a long-term server outage.
-        *
-        * FIXME: WTF? Why is it using a loop and then returning unconditionally?
-        * @param $id int
-        * @return bool|string
-        */
-       private function getTextDbSafe( $id ) {
-               while ( true ) {
-                       try {
-                               $text = $this->getTextDb( $id );
-                       } catch ( DBQueryError $ex ) {
-                               $text = false;
-                       }
-                       return $text;
+               // Retirieving a good text for $id failed (at least) maxFailures times.
+               // We abort for this $id.
+
+               // Restoring the consecutive failures, and maybe aborting, if the dump
+               // is too broken.
+               $consecutiveFailedTextRetrievals = $oldConsecutiveFailedTextRetrievals + 1;
+               if ( $consecutiveFailedTextRetrievals > $this->maxConsecutiveFailedTextRetrievals ) {
+                       throw new MWException( "Graceful storage failure" );
                }
+
+               return "";
        }
 
+
        /**
         * May throw a database error if, say, the server dies during query.
         * @param $id
         * @return bool|string
+        * @throws MWException
         */
        private function getTextDb( $id ) {
                global $wgContLang;
+               if ( ! isset( $this->db ) ) {
+                       throw new MWException( __METHOD__ . "No database available" );
+               }
                $row = $this->db->selectRow( 'text',
                        array( 'old_text', 'old_flags' ),
                        array( 'old_id' => $id ),
@@ -517,7 +617,7 @@ class TextPassDumper extends BackupDumper {
 
                $nbytes = intval( $len );
                // actual error, not zero-length text
-               if ($nbytes < 0 ) return false;
+               if ( $nbytes < 0 ) return false;
 
                $text = "";
 
@@ -584,15 +684,15 @@ class TextPassDumper extends BackupDumper {
                        $this->buffer = "";
                        $this->thisRev = "";
                } elseif ( $name == 'page' ) {
-                       if (! $this->firstPageWritten) {
-                               $this->firstPageWritten = trim($this->thisPage);
+                       if ( ! $this->firstPageWritten ) {
+                               $this->firstPageWritten = trim( $this->thisPage );
                        }
-                       $this->lastPageWritten = trim($this->thisPage);
-                       if ($this->timeExceeded) {
+                       $this->lastPageWritten = trim( $this->thisPage );
+                       if ( $this->timeExceeded ) {
                                $this->egress->writeClosePage( $this->buffer );
                                // nasty hack, we can't just write the chardata after the
                                // page tag, it will include leading blanks from the next line
-                               $this->egress->sink->write("\n");
+                               $this->egress->sink->write( "\n" );
 
                                $this->buffer = $this->xmlwriterobj->closeStream();
                                $this->egress->writeCloseStream( $this->buffer );
@@ -603,11 +703,11 @@ class TextPassDumper extends BackupDumper {
 
                                $filenameList = (array)$this->egress->getFilenames();
                                $newFilenames = array();
-                               $firstPageID = str_pad($this->firstPageWritten,9,"0",STR_PAD_LEFT);
-                               $lastPageID = str_pad($this->lastPageWritten,9,"0",STR_PAD_LEFT);
+                               $firstPageID = str_pad( $this->firstPageWritten, 9, "0", STR_PAD_LEFT );
+                               $lastPageID = str_pad( $this->lastPageWritten, 9, "0", STR_PAD_LEFT );
                                for ( $i = 0; $i < count( $filenameList ); $i++ ) {
                                        $checkpointNameFilledIn = sprintf( $this->checkpointFiles[$i], $firstPageID, $lastPageID );
-                                       $fileinfo = pathinfo($filenameList[$i]);
+                                       $fileinfo = pathinfo( $filenameList[$i] );
                                        $newFilenames[] = $fileinfo['dirname'] . '/' . $checkpointNameFilledIn;
                                }
                                $this->egress->closeRenameAndReopen( $newFilenames );
@@ -640,9 +740,9 @@ class TextPassDumper extends BackupDumper {
                }
                // have to skip the newline left over from closepagetag line of
                // end of checkpoint files. nasty hack!!
-               if ($this->checkpointJustWritten) {
-                       if ($data[0] == "\n") {
-                               $data = substr($data,1);
+               if ( $this->checkpointJustWritten ) {
+                       if ( $data[0] == "\n" ) {
+                               $data = substr( $data, 1 );
                        }
                        $this->checkpointJustWritten = false;
                }
index c9afbfa..d621686 100644 (file)
@@ -408,6 +408,7 @@ $wgMessageStructure = array(
                'customjsprotected',
                'ns-specialprotected',
                'titleprotected',
+               'filereadonlyerror'
        ),
        'virus' => array(
                'virus-badscanner',
index 60fc7fc..66a1845 100644 (file)
@@ -326,7 +326,7 @@ CREATE INDEX /*i*/rev_timestamp ON /*_*/revision (rev_timestamp);
 CREATE INDEX /*i*/page_timestamp ON /*_*/revision (rev_page,rev_timestamp);
 CREATE INDEX /*i*/user_timestamp ON /*_*/revision (rev_user,rev_timestamp);
 CREATE INDEX /*i*/usertext_timestamp ON /*_*/revision (rev_user_text,rev_timestamp);
-CREATE INDEX /*i*/page_user_timestamp ON /*_*/revision  (rev_page,rev_user,rev_timestamp);
+CREATE INDEX /*i*/page_user_timestamp ON /*_*/revision (rev_page,rev_user,rev_timestamp);
 
 --
 -- Holds text of individual page revisions.
@@ -946,44 +946,44 @@ CREATE INDEX /*i*/fa_user_timestamp ON /*_*/filearchive (fa_user_text,fa_timesta
 -- moved into the actual filestore
 --
 CREATE TABLE /*_*/uploadstash (
-       us_id int unsigned NOT NULL PRIMARY KEY auto_increment,
+  us_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
 
-       -- the user who uploaded the file.
-       us_user int unsigned NOT NULL,
+  -- the user who uploaded the file.
+  us_user int unsigned NOT NULL,
 
-       -- file key. this is how applications actually search for the file.
-       -- this might go away, or become the primary key.
-       us_key varchar(255) NOT NULL,
+  -- file key. this is how applications actually search for the file.
+  -- this might go away, or become the primary key.
+  us_key varchar(255) NOT NULL,
 
-       -- the original path
-       us_orig_path varchar(255) NOT NULL,
+  -- the original path
+  us_orig_path varchar(255) NOT NULL,
 
-       -- the temporary path at which the file is actually stored
-       us_path varchar(255) NOT NULL,
+  -- the temporary path at which the file is actually stored
+  us_path varchar(255) NOT NULL,
 
-       -- which type of upload the file came from (sometimes)
-       us_source_type varchar(50),
+  -- which type of upload the file came from (sometimes)
+  us_source_type varchar(50),
 
-       -- the date/time on which the file was added
-       us_timestamp varbinary(14) not null,
+  -- the date/time on which the file was added
+  us_timestamp varbinary(14) NOT NULL,
 
-       us_status varchar(50) not null,
+  us_status varchar(50) NOT NULL,
 
-       -- chunk counter starts at 0, current offset is stored in us_size
-       us_chunk_inx int unsigned NULL,
+  -- chunk counter starts at 0, current offset is stored in us_size
+  us_chunk_inx int unsigned NULL,
 
-       -- file properties from File::getPropsFromPath.  these may prove unnecessary.
-       --
-       us_size int unsigned NOT NULL,
-       -- this hash comes from File::sha1Base36(), and is 31 characters
-       us_sha1 varchar(31) NOT NULL,
-       us_mime varchar(255),
-       -- Media type as defined by the MEDIATYPE_xxx constants, should duplicate definition in the image table
-       us_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
-       -- image-specific properties
-       us_image_width int unsigned,
-       us_image_height int unsigned,
-       us_image_bits smallint unsigned
+  -- file properties from File::getPropsFromPath.  these may prove unnecessary.
+  --
+  us_size int unsigned NOT NULL,
+  -- this hash comes from File::sha1Base36(), and is 31 characters
+  us_sha1 varchar(31) NOT NULL,
+  us_mime varchar(255),
+  -- Media type as defined by the MEDIATYPE_xxx constants, should duplicate definition in the image table
+  us_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+  -- image-specific properties
+  us_image_width int unsigned,
+  us_image_height int unsigned,
+  us_image_bits smallint unsigned
 
 ) /*$wgDBTableOptions*/;
 
@@ -1237,7 +1237,7 @@ CREATE INDEX /*i*/page_time ON /*_*/logging (log_namespace, log_title, log_times
 CREATE INDEX /*i*/times ON /*_*/logging (log_timestamp);
 CREATE INDEX /*i*/log_user_type_time ON /*_*/logging (log_user, log_type, log_timestamp);
 CREATE INDEX /*i*/log_page_id_time ON /*_*/logging (log_page,log_timestamp);
-CREATE INDEX /*i*/type_action ON /*_*/logging(log_type, log_action, log_timestamp);
+CREATE INDEX /*i*/type_action ON /*_*/logging (log_type, log_action, log_timestamp);
 
 
 CREATE TABLE /*_*/log_search (
@@ -1275,7 +1275,7 @@ CREATE TABLE /*_*/job (
 ) /*$wgDBTableOptions*/;
 
 CREATE INDEX /*i*/job_cmd ON /*_*/job (job_cmd, job_namespace, job_title, job_params(128));
-CREATE INDEX /*i*/job_timestamp ON /*_*/job(job_timestamp);
+CREATE INDEX /*i*/job_timestamp ON /*_*/job (job_timestamp);
 
 
 -- Details of updates to cached special pages
index c65be69..edfae92 100644 (file)
@@ -10,7 +10,7 @@ define( 'MEDIAWIKI_INSTALL', true );
 
 chdir( dirname( dirname( __FILE__ ) ) );
 if ( isset( $_SERVER['MW_COMPILED'] ) ) {
-       require ( 'phase3/includes/WebStart.php' );
+       require ( 'core/includes/WebStart.php' );
 } else {
        require( dirname( dirname( __FILE__ ) ) . '/includes/WebStart.php' );
 }
index 1549349..ef038c1 100644 (file)
@@ -29,7 +29,7 @@ ini_set( 'zlib.output_compression', 'off' );
 
 $wgEnableProfileInfo = $wgProfileToDatabase = false;
 if ( isset( $_SERVER['MW_COMPILED'] ) ) {
-       require ( 'phase3/includes/WebStart.php' );
+       require ( 'core/includes/WebStart.php' );
 } else {
        require ( dirname( __FILE__ ) . '/includes/WebStart.php' );
 }
@@ -56,16 +56,20 @@ header( 'Content-Type: text/html; charset=utf-8' );
                text-align: right;
        }
        td.timep, td.tpc, td.tpr {
-               background-color: #fffff0;
+               background-color: #ffff80;
        }
        td.memoryp, td.mpc, td.mpr {
-               background-color: #f0f8ff;
+               background-color: #80f8ff;
        }
        td.count, td,cpr {
-               background-color: #f0fff0;
+               background-color: #80ff80;
        }
        td.name {
-               background-color: #f9f9f9;
+               background-color: #89f9f9;
+       }
+
+       tr:hover {
+               font-weight: bold;
        }
 </style>
 </head>
index f5f09f5..090e4c3 100644 (file)
  */
 ( function ( $, mw, undefined ) {
 
-/**
- * The name of the page to watch or unwatch.
- */
-var title = mw.config.get( 'wgRelevantPageName', mw.config.get( 'wgPageName' ) );
-
-/**
- * Update the link text, link href attribute and (if applicable)
- * "loading" class.
- *
- * @param $link {jQuery} Anchor tag of (un)watch link
- * @param action {String} One of 'watch', 'unwatch'.
- * @param state {String} [optional] 'idle' or 'loading'. Default is 'idle'.
- */
-function updateWatchLink( $link, action, state ) {
-       // message keys 'watch', 'watching', 'unwatch' or 'unwatching'.
-       var     msgKey = state === 'loading' ? action + 'ing' : action,
-               accesskeyTip = $link.attr( 'title' ).match( mw.util.tooltipAccessKeyRegexp ),
+       /**
+        * The name of the page to watch or unwatch.
+        */
+       var title = mw.config.get( 'wgRelevantPageName', mw.config.get( 'wgPageName' ) );
+
+       /**
+        * Update the link text, link href attribute and (if applicable)
+        * "loading" class.
+        *
+        * @param $link {jQuery} Anchor tag of (un)watch link.
+        * @param action {String} One of 'watch', 'unwatch'.
+        * @param state {String} [optional] 'idle' or 'loading'. Default is 'idle'.
+        */
+       function updateWatchLink( $link, action, state ) {
+               var accesskeyTip, msgKey, $li;
+
+               // message keys 'watch', 'watching', 'unwatch' or 'unwatching'.
+               msgKey = state === 'loading' ? action + 'ing' : action;
+               accesskeyTip = $link.attr( 'title' ).match( mw.util.tooltipAccessKeyRegexp );
                $li = $link.closest( 'li' );
 
-       $link
-               .text( mw.msg( msgKey ) )
-               .attr( 'title', mw.msg( 'tooltip-ca-' + action ) +
-                       ( accesskeyTip ? ' ' + accesskeyTip[0] : '' )
-               )
-               .attr( 'href', mw.util.wikiScript() + '?' + $.param({
-                               title: title,
-                               action: action
-                       })
-               );
-
-       // Special case for vector icon
-       if ( $li.hasClass( 'icon' ) ) {
-               if ( state === 'loading' ) {
-                       $link.addClass( 'loading' );
-               } else {
-                       $link.removeClass( 'loading' );
+               $link
+                       .text( mw.msg( msgKey ) )
+                       .attr( 'title', mw.msg( 'tooltip-ca-' + action ) +
+                               ( accesskeyTip ? ' ' + accesskeyTip[0] : '' )
+                       )
+                       .attr( 'href', mw.util.wikiScript() + '?' + $.param({
+                                       title: title,
+                                       action: action
+                               })
+                       );
+
+               // Special case for vector icon
+               if ( $li.hasClass( 'icon' ) ) {
+                       if ( state === 'loading' ) {
+                               $link.addClass( 'loading' );
+                       } else {
+                               $link.removeClass( 'loading' );
+                       }
                }
        }
-}
 
-/**
- * @todo This should be moved somewhere more accessible.
- * @param url {String}
- * @return {String} The extracted action, defaults to 'view'.
- */
-function mwUriGetAction( url ) {
-       var     actionPaths = mw.config.get( 'wgActionPaths' ),
-               key, parts, m, action;
-
-       // @todo: Does MediaWiki give action path or query param
-       // precedence ? If the former, move this to the bottom
-       action = mw.util.getParamValue( 'action', url );
-       if ( action !== null ) {
-               return action;
-       }
+       /**
+        * @todo This should be moved somewhere more accessible.
+        * @param url {String}
+        * @return {String} The extracted action, defaults to 'view'.
+        */
+       function mwUriGetAction( url ) {
+               var action, actionPaths, key, i, m, parts;
+
+               actionPaths = mw.config.get( 'wgActionPaths' );
+
+               // @todo: Does MediaWiki give action path or query param
+               // precedence ? If the former, move this to the bottom
+               action = mw.util.getParamValue( 'action', url );
+               if ( action !== null ) {
+                       return action;
+               }
+
+               for ( key in actionPaths ) {
+                       if ( actionPaths.hasOwnProperty( key ) ) {
+                               parts = actionPaths[key].split( '$1' );
+                               for ( i = 0; i < parts.length; i += 1 ) {
+                                       parts[i] = $.escapeRE( parts[i] );
+                               }
+                               m = new RegExp( parts.join( '(.+)' ) ).exec( url );
+                               if ( m && m[1] ) {
+                                       return key;
+                               }
 
-       for ( key in actionPaths ) {
-               if ( actionPaths.hasOwnProperty( key ) ) {
-                       parts = actionPaths[key].split( '$1' );
-                       for ( i = 0; i < parts.length; i += 1 ) {
-                               parts[i] = $.escapeRE( parts[i] );
-                       }
-                       m = new RegExp( parts.join( '(.+)' ) ).exec( url );
-                       if ( m && m[1] ) {
-                               return key;
                        }
-               
                }
+
+               return 'view';
        }
 
-       return 'view';
-}
+       $( document ).ready( function () {
+               var $links = $( '.mw-watchlink a, a.mw-watchlink, ' +
+                       '#ca-watch a, #ca-unwatch a, #mw-unwatch-link1, ' +
+                       '#mw-unwatch-link2, #mw-watch-link2, #mw-watch-link1' );
 
-$( document ).ready( function() {
-       var $links = $( '.mw-watchlink a, a.mw-watchlink, ' +
-               '#ca-watch a, #ca-unwatch a, #mw-unwatch-link1, ' +
-               '#mw-unwatch-link2, #mw-watch-link2, #mw-watch-link1' );
+               // Allowing people to add inline animated links is a little scary
+               $links = $links.filter( ':not( #bodyContent *, #content * )' );
 
-       // Allowing people to add inline animated links is a little scary
-       $links = $links.filter( ':not( #bodyContent *, #content * )' );
+               $links.click( function ( e ) {
+                       var action, api, $link;
 
-       $links.click( function( e ) {
-               var     $link, api,
                        action = mwUriGetAction( this.href );
 
-               if ( action !== 'watch' && action !== 'unwatch' ) {
-                       // Could not extract target action from link url,
-                       // let native browsing handle it further
-                       return true;
-               }
-               e.preventDefault();
-               e.stopPropagation();
-               
-               $link = $( this );
-
-               updateWatchLink( $link, action, 'loading' );
-
-               api = new mw.Api();
-               api[action](
-                       title,
-                       // Success
-                       function( watchResponse ) {
-                               var     otherAction = action === 'watch' ? 'unwatch' : 'watch',
-                                       $li = $link.closest( 'li' );
+                       if ( action !== 'watch' && action !== 'unwatch' ) {
+                               // Could not extract target action from link url,
+                               // let native browsing handle it further
+                               return true;
+                       }
+                       e.preventDefault();
+                       e.stopPropagation();
 
-                               mw.util.jsMessage( watchResponse.message, 'ajaxwatch' );
+                       $link = $( this );
 
-                               // Set link to opposite
-                               updateWatchLink( $link, otherAction );
+                       updateWatchLink( $link, action, 'loading' );
 
-                               // Most common ID style
-                               if ( $li.prop( 'id' ) === 'ca-' + otherAction || $li.prop( 'id' ) === 'ca-' + action ) {
-                                       $li.prop( 'id', 'ca-' + otherAction );
-                               }
-                               
-                               // Bug 12395 - update the watch checkbox on edit pages when the
-                               // page is watched or unwatched via the tab.
-                               if ( watchResponse.watched !== undefined ) {
-                                       $( '#wpWatchthis' ).prop( 'checked', true );
-                               } else {
-                                       $( '#wpWatchthis' ).removeProp( 'checked' );
-                               }
-                       },
-                       // Error
-                       function(){             
-
-                               // Reset link to non-loading mode
-                               updateWatchLink( $link, action );
-                               
-                               // Format error message
-                               var cleanTitle = title.replace( /_/g, ' ' );
-                               var link = mw.html.element(
-                                       'a', {
-                                               'href': mw.util.wikiGetlink( title ),
-                                               'title': cleanTitle
-                                       }, cleanTitle
-                               );
-                               var html = mw.msg( 'watcherrortext', link );
-                               
-                               // Report to user about the error
-                               mw.util.jsMessage( html, 'ajaxwatch' );
+                       api = new mw.Api();
+                       api[action](
+                               title,
+                               // Success
+                               function ( watchResponse ) {
+                                       var $li, otherAction;
 
-                       }
-               );
-       });
+                                       otherAction = action === 'watch' ? 'unwatch' : 'watch';
+                                       $li = $link.closest( 'li' );
 
-});
+                                       mw.util.jsMessage( watchResponse.message, 'ajaxwatch' );
+
+                                       // Set link to opposite
+                                       updateWatchLink( $link, otherAction );
+
+                                       // Most common ID style
+                                       if ( $li.prop( 'id' ) === 'ca-' + otherAction || $li.prop( 'id' ) === 'ca-' + action ) {
+                                               $li.prop( 'id', 'ca-' + otherAction );
+                                       }
+
+                                       // Bug 12395 - update the watch checkbox on edit pages when the
+                                       // page is watched or unwatched via the tab.
+                                       if ( watchResponse.watched !== undefined ) {
+                                               $( '#wpWatchthis' ).prop( 'checked', true );
+                                       } else {
+                                               $( '#wpWatchthis' ).removeProp( 'checked' );
+                                       }
+                               },
+                               // Error
+                               function () {
+                                       var cleanTitle, html, link;
+
+                                       // Reset link to non-loading mode
+                                       updateWatchLink( $link, action );
+
+                                       // Format error message
+                                       cleanTitle = title.replace( /_/g, ' ' );
+                                       link = mw.html.element(
+                                               'a', {
+                                                       href: mw.util.wikiGetlink( title ),
+                                                       title: cleanTitle
+                                               }, cleanTitle
+                                       );
+                                       html = mw.msg( 'watcherrortext', link );
+
+                                       // Report to user about the error
+                                       mw.util.jsMessage( html, 'ajaxwatch' );
+
+                               }
+                       );
+               });
+       });
 
-})( jQuery, mediaWiki );
+}( jQuery, mediaWiki ) );
index 8ed6832..302a2d3 100644 (file)
@@ -4,47 +4,51 @@
  */
 ( function( $, mw ) {
 
+       /**
+        * @context {mw.Api}
+        */
+       function doWatchInternal( page, success, err, addParams ) {
+               var params = {
+                       action: 'watch',
+                       title: String( page ),
+                       token: mw.user.tokens.get( 'watchToken' ),
+                       uselang: mw.config.get( 'wgUserLanguage' )
+               };
+               function ok( data ) {
+                       success( data.watch );
+               }
+               if ( addParams ) {
+                       $.extend( params, addParams );
+               }
+               return this.post( params, { ok: ok, err: err } );
+       }
+
        $.extend( mw.Api.prototype, {
                /**
                 * Convinience method for 'action=watch'.
                 *
                 * @param page {String|mw.Title} Full page name or instance of mw.Title
-                * @param success {Function} callback to which the watch object will be passed
-                * watch object contains 'title' (full page name), 'watched' (boolean) and
+                * @param success {Function} Callback to which the watch object will be passed.
+                * Watch object contains properties 'title' (full pagename), 'watched' (boolean) and
                 * 'message' (parsed HTML of the 'addedwatchtext' message).
-                * @param _unwatch {Boolean} Internally used to re-use this logic for unwatch(),
-                * do not use outside this module.
-                * @param err {Function} callback if error (optional)
+                * @param err {Function} Error callback (optional)
                 * @return {jqXHR}
                 */
-               watch: function( page, success, err, _unwatch ) {
-                       var params, ok;
-                       params = {
-                               action: 'watch',
-                               title: String( page ),
-                               token: mw.user.tokens.get( 'watchToken' ),
-                               uselang: mw.config.get( 'wgUserLanguage' )
-                       };
-                       if ( _unwatch ) {
-                               params.unwatch = 1;
-                       }
-                       ok = function( data ) {
-                               success( data.watch );
-                       };
-                       return this.post( params, { ok: ok, err: err } );
+               watch: function ( page, success, err ) {
+                       return doWatchInternal.call( this, page, success, err );
                },
                /**
                 * Convinience method for 'action=watch&unwatch=1'.
                 *
                 * @param page {String|mw.Title} Full page name or instance of mw.Title
-                * @param success {Function} callback to which the watch object will be passed
-                * watch object contains 'title' (full page name), 'unwatched' (boolean) and
+                * @param success {Function} Callback to which the watch object will be passed.
+                * Watch object contains properties 'title' (full pagename), 'watched' (boolean) and
                 * 'message' (parsed HTML of the 'removedwatchtext' message).
-                * @param err {Function} callback if error (optional)
+                * @param err {Function} Error callback (optional)
                 * @return {jqXHR}
                 */
-               unwatch: function( page, success, err ) {
-                       return this.watch( page, success, err, true );
+               unwatch: function ( page, success, err ) {
+                       return doWatchInternal.call( this, page, success, err, { unwatch: 1 } );
                }
 
        } );
index 501a267..d1b51a6 100644 (file)
@@ -72,7 +72,7 @@ class VectorTemplate extends BaseTemplate {
                $nav = $this->data['content_navigation'];
 
                if ( $wgVectorUseIconWatch ) {
-                       $mode = $this->getSkin()->getTitle()->userIsWatching() ? 'unwatch' : 'watch';
+                       $mode = $this->getSkin()->getRelevantTitle()->userIsWatching() ? 'unwatch' : 'watch';
                        if ( isset( $nav['actions'][$mode] ) ) {
                                $nav['views'][$mode] = $nav['actions'][$mode];
                                $nav['views'][$mode]['class'] = rtrim( 'icon ' . $nav['views'][$mode]['class'], ' ' );
index 7865ed2..fbf271c 100644 (file)
@@ -1,4 +1,7 @@
 <?php
+/**
+ * @group Database
+ */
 class RecentChangeTest extends MediaWikiTestCase {
        protected $title;
        protected $target;
index c83e01e..c089c31 100644 (file)
@@ -23,12 +23,12 @@ class LanguageTest extends MediaWikiTestCase {
                        'convertDoubleWidth() with the full alphabet and digits'
                );
        }
-       
+
        /** @dataProvider provideFormattableTimes */
        function testFormatTimePeriod( $seconds, $format, $expected, $desc ) {
                $this->assertEquals( $expected, $this->lang->formatTimePeriod( $seconds, $format ), $desc );
        }
-       
+
        function provideFormattableTimes() {
                return array(
                        array( 9.45, array(), '9.5s', 'formatTimePeriod() rounding (<10s)' ),
@@ -62,7 +62,7 @@ class LanguageTest extends MediaWikiTestCase {
                        array( 176460.55, array(), '2d 1h 1m 1s', 'formatTimePeriod() rounding, recursion, (>48h)' ),
                        array( 176460.55, array( 'noabbrevs' => true ), '2 days 1 hour 1 minute 1 second', 'formatTimePeriod() rounding, recursion, (>48h)' ),
                );
-               
+
        }
 
        function testTruncate() {
@@ -224,7 +224,7 @@ class LanguageTest extends MediaWikiTestCase {
                        "sprintfDate('$format', '$ts'): $msg"
                );
 
-               date_default_timezone_set( $oldTZ );            
+               date_default_timezone_set( $oldTZ );
        }
 
        function provideSprintfDateSamples() {
index 83d1a34..bc880a8 100644 (file)
@@ -1,6 +1,6 @@
 == Details==
 
-Automated Selenium test scripts written for MediaWiki Installer is available at svn.wikimedia.org/svnroot/mediawiki/trunk/phase3/tests/selenium/installer.
+Automated Selenium test scripts written for MediaWiki Installer is available at https://gerrit.wikimedia.org/r/gitweb?p=mediawiki/core.git;a=tree;f=tests/selenium/installer;hb=HEAD.
 Detailed test cases available at http://www.mediawiki.org/wiki/New_installer/Test_plan.
 
 Version : MediaWiki 1.18alpha
index eb0d67d..6afc7e5 100644 (file)
--- a/thumb.php
+++ b/thumb.php
@@ -8,7 +8,7 @@
  */
 define( 'MW_NO_OUTPUT_COMPRESSION', 1 );
 if ( isset( $_SERVER['MW_COMPILED'] ) ) {
-       require( 'phase3/includes/WebStart.php' );
+       require( 'core/includes/WebStart.php' );
 } else {
        require( dirname( __FILE__ ) . '/includes/WebStart.php' );
 }