r114335 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r114334‎ | r114335 | r114336 >
Date:00:16, 21 March 2012
Author:catrope
Status:ok
Tags:
Comment:
Revert r107309, r113601, r113704, r113742, r113792, r113838, r113859, r113893, r113894, r113952, r114047, r114252, r114256, r114257. This reverts the remaining 'new' revisions in core.

All of these revisions are tagged with 'gerritmigration' and will be resubmitted into Gerrit after the Gerrit switchover. See also http://lists.wikimedia.org/pipermail/wikitech-l/2012-March/059124.html
Modified paths:
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/api/ApiFeedContributions.php (modified) (history)
  • /trunk/phase3/includes/db/Database.php (modified) (history)
  • /trunk/phase3/includes/db/DatabasePostgres.php (modified) (history)
  • /trunk/phase3/includes/filerepo/FileRepo.php (modified) (history)
  • /trunk/phase3/includes/filerepo/backend/FileBackend.php (modified) (history)
  • /trunk/phase3/includes/filerepo/backend/FileBackendMultiWrite.php (modified) (history)
  • /trunk/phase3/includes/filerepo/backend/FileBackendStore.php (modified) (history)
  • /trunk/phase3/includes/filerepo/backend/FileOp.php (modified) (history)
  • /trunk/phase3/includes/filerepo/backend/filejournal (deleted) (history)
  • /trunk/phase3/includes/filerepo/file/File.php (modified) (history)
  • /trunk/phase3/includes/filerepo/file/LocalFile.php (modified) (history)
  • /trunk/phase3/includes/installer/DatabaseInstaller.php (modified) (history)
  • /trunk/phase3/includes/installer/PostgresUpdater.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialBlock.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesEn.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesFrp.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesIg.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesMk.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesMzn.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesNds_nl.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesOr.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesOs.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesQug.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesSa.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesSr_ec.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesWar.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesYue.php (modified) (history)
  • /trunk/phase3/maintenance/archives/patch-filejournal.sql (deleted) (history)
  • /trunk/phase3/maintenance/dumpTextPass.php (modified) (history)
  • /trunk/phase3/maintenance/language/messages.inc (modified) (history)
  • /trunk/phase3/resources/mediawiki.action/mediawiki.action.watch.ajax.js (modified) (history)
  • /trunk/phase3/resources/mediawiki.api/mediawiki.api.watch.js (modified) (history)

Diff [purge]

Index: trunk/phase3/maintenance/archives/patch-filejournal.sql
@@ -1,24 +0,0 @@
2 -CREATE TABLE /*_*/filejournal (
3 - -- Unique ID for each file operation
4 - fj_id bigint unsigned NOT NULL PRIMARY KEY auto_increment,
5 - -- UUID of the batch this operation belongs to
6 - fj_batch_uuid varbinary(32) NOT NULL,
7 - -- The registered file backend name
8 - fj_backend varchar(255) NOT NULL,
9 - -- The storage path that was affected (may be internal paths)
10 - fj_path blob NOT NULL,
11 - -- SHA-1 file path hash in base-36
12 - fj_path_sha1 varbinary(32) NOT NULL default '',
13 - -- Primitive operation description (create/update/delete)
14 - fj_op varchar(16) NOT NULL default '',
15 - -- SHA-1 file content hash in base-36
16 - fj_new_sha1 varbinary(32) NOT NULL default '',
17 - -- Timestamp of the batch operation
18 - fj_timestamp varbinary(14) NOT NULL default ''
19 -);
20 -
21 -CREATE INDEX /*i*/fj_batch_id ON /*_*/filejournal (fj_batch_uuid,fj_id);
22 -CREATE INDEX /*i*/fj_path_id ON /*_*/filejournal (fj_path_sha1,fj_id);
23 -CREATE INDEX /*i*/fj_new_sha1 ON /*_*/filejournal (fj_new_sha1,fj_id);
24 -CREATE INDEX /*i*/fj_timestamp ON /*_*/filejournal (fj_timestamp);
Index: trunk/phase3/maintenance/language/messages.inc
@@ -408,7 +408,6 @@
409409 'customjsprotected',
410410 'ns-specialprotected',
411411 'titleprotected',
412 - 'filereadonlyerror'
413412 ),
414413 'virus' => array(
415414 'virus-badscanner',
@@ -1378,11 +1377,6 @@
13791378 'backend-fail-batchsize'
13801379 ),
13811380
1382 - 'filejournal-errors' => array(
1383 - 'filejournal-fail-dbconnect',
1384 - 'filejournal-fail-dbquery'
1385 - ),
1386 -
13871381 'lockmanager-errors' => array(
13881382 'lockmanager-notlocked',
13891383 'lockmanager-fail-closelock',
Index: trunk/phase3/maintenance/dumpTextPass.php
@@ -41,7 +41,9 @@
4242 var $prefetchCountLast = 0;
4343 var $fetchCountLast = 0;
4444
 45+ var $failures = 0;
4546 var $maxFailures = 5;
 47+ var $failedTextRetrievals = 0;
4648 var $maxConsecutiveFailedTextRetrievals = 200;
4749 var $failureTimeout = 5; // Seconds to sleep after db failure
4850
@@ -69,54 +71,6 @@
7072 */
7173 protected $db;
7274
73 -
74 - /**
75 - * Drop the database connection $this->db and try to get a new one.
76 - *
77 - * This function tries to get a /different/ connection if this is
78 - * possible. Hence, (if this is possible) it switches to a different
79 - * failover upon each call.
80 - *
81 - * This function resets $this->lb and closes all connections on it.
82 - *
83 - * @throws MWException
84 - */
85 - function rotateDb() {
86 - // Cleaning up old connections
87 - if ( isset( $this->lb ) ) {
88 - $this->lb->closeAll();
89 - unset( $this->lb );
90 - }
91 -
92 - if ( isset( $this->db ) && $this->db->isOpen() )
93 - {
94 - throw new MWException( 'DB is set and has not been closed by the Load Balancer' );
95 - }
96 -
97 -
98 - unset( $this->db );
99 -
100 - // Trying to set up new connection.
101 - // We do /not/ retry upon failure, but delegate to encapsulating logic, to avoid
102 - // individually retrying at different layers of code.
103 -
104 - // 1. The LoadBalancer.
105 - try {
106 - $this->lb = wfGetLBFactory()->newMainLB();
107 - } catch (Exception $e) {
108 - throw new MWException( __METHOD__ . " rotating DB failed to obtain new load balancer (" . $e->getMessage() . ")" );
109 - }
110 -
111 -
112 - // 2. The Connection, through the load balancer.
113 - try {
114 - $this->db = $this->lb->getConnection( DB_SLAVE, 'backup' );
115 - } catch (Exception $e) {
116 - throw new MWException( __METHOD__ . " rotating DB failed to obtain new database (" . $e->getMessage() . ")" );
117 - }
118 - }
119 -
120 -
12175 function initProgress( $history ) {
12276 parent::initProgress();
12377 $this->timeOfCheckpoint = $this->startTime;
@@ -133,19 +87,7 @@
13488
13589 $this->initProgress( $this->history );
13690
137 - // We are trying to get an initial database connection to avoid that the
138 - // first try of this request's first call to getText fails. However, if
139 - // obtaining a good DB connection fails it's not a serious issue, as
140 - // getText does retry upon failure and can start without having a working
141 - // DB connection.
142 - try {
143 - $this->rotateDb();
144 - } catch (Exception $e) {
145 - // We do not even count this as failure. Just let eventual
146 - // watchdogs know.
147 - $this->progress( "Getting initial DB connection failed (" .
148 - $e->getMessage() . ")" );
149 - }
 91+ $this->db = $this->backupDb();
15092
15193 $this->egress = new ExportProgressFilter( $this->sink, $this );
15294
@@ -374,142 +316,98 @@
375317 return true;
376318 }
377319
378 - /**
379 - * Tries to get the revision text for a revision id.
380 - *
381 - * Upon errors, retries (Up to $this->maxFailures tries each call).
382 - * If still no good revision get could be found even after this retrying, "" is returned.
383 - * If no good revision text could be returned for
384 - * $this->maxConsecutiveFailedTextRetrievals consecutive calls to getText, MWException
385 - * is thrown.
386 - *
387 - * @param $id string The revision id to get the text for
388 - *
389 - * @return string The revision text for $id, or ""
390 - * @throws MWException
391 - */
392320 function getText( $id ) {
393 - $prefetchNotTried = true; // Whether or not we already tried to get the text via prefetch.
394 - $text = false; // The candidate for a good text. false if no proper value.
395 - $failures = 0; // The number of times, this invocation of getText already failed.
396 -
397 - static $consecutiveFailedTextRetrievals = 0; // The number of times getText failed without
398 - // yielding a good text in between.
399 -
400321 $this->fetchCount++;
401 -
402 - // To allow to simply return on success and do not have to worry about book keeping,
403 - // we assume, this fetch works (possible after some retries). Nevertheless, we koop
404 - // the old value, so we can restore it, if problems occur (See after the while loop).
405 - $oldConsecutiveFailedTextRetrievals = $consecutiveFailedTextRetrievals;
406 - $consecutiveFailedTextRetrievals = 0;
407 -
408 - while ( $failures < $this->maxFailures ) {
409 -
410 - // As soon as we found a good text for the $id, we will return immediately.
411 - // Hence, if we make it past the try catch block, we know that we did not
412 - // find a good text.
413 -
414 - try {
415 - // Step 1: Get some text (or reuse from previous iteratuon if checking
416 - // for plausibility failed)
417 -
418 - // Trying to get prefetch, if it has not been tried before
419 - if ( $text === false && isset( $this->prefetch ) && $prefetchNotTried ) {
420 - $prefetchNotTried = false;
421 - $tryIsPrefetch = true;
422 - $text = $this->prefetch->prefetch( $this->thisPage, $this->thisRev );
423 - if ( $text === null ) {
424 - $text = false;
425 - }
426 - }
427 -
428 - if ( $text === false ) {
429 - // Fallback to asking the database
430 - $tryIsPrefetch = false;
431 - if ( $this->spawn ) {
432 - $text = $this->getTextSpawned( $id );
433 - } else {
434 - $text = $this->getTextDb( $id );
435 - }
436 - }
437 -
438 - if ( $text === false ) {
439 - throw new MWException( "Generic error while obtaining text for id " . $id );
440 - }
441 -
442 - // We received a good candidate for the text of $id via some method
443 -
444 - // Step 2: Checking for plausibility and return the text if it is
445 - // plausible
 322+ if ( isset( $this->prefetch ) ) {
 323+ $text = $this->prefetch->prefetch( $this->thisPage, $this->thisRev );
 324+ if ( $text !== null ) { // Entry missing from prefetch dump
 325+ $dbr = wfGetDB( DB_SLAVE );
446326 $revID = intval( $this->thisRev );
447 - if ( ! isset( $this->db ) ) {
448 - throw new MWException( "No database available" );
449 - }
450 - $revLength = $this->db->selectField( 'revision', 'rev_len', array( 'rev_id' => $revID ) );
 327+ $revLength = $dbr->selectField( 'revision', 'rev_len', array( 'rev_id' => $revID ) );
 328+ // if length of rev text in file doesn't match length in db, we reload
 329+ // this avoids carrying forward broken data from previous xml dumps
451330 if( strlen( $text ) == $revLength ) {
452 - if ( $tryIsPrefetch ) {
453 - $this->prefetchCount++;
454 - }
 331+ $this->prefetchCount++;
455332 return $text;
456333 }
457 -
458 - $text = false;
459 - throw new MWException( "Received text is unplausible for id " . $id );
460 -
461 - } catch (Exception $e) {
462 - $msg = "getting/checking text " . $id . " failed (".$e->getMessage().")";
463 - if ( $failures + 1 < $this->maxFailures ) {
464 - $msg .= " (Will retry " . ( $this->maxFailures - $failures - 1) . " more times)";
465 - }
466 - $this->progress( $msg );
467334 }
468 -
469 - // Something went wrong; we did not a text that was plausible :(
470 - $failures++;
 335+ }
 336+ return $this->doGetText( $id );
 337+ }
471338
472 -
473 - // After backing off for some time, we try to reboot the whole process as
474 - // much as possible to not carry over failures from one part to the other
475 - // parts
476 - sleep( $this->failureTimeout );
477 - try {
478 - $this->rotateDb();
479 - if ( $this->spawn ) {
 339+ private function doGetText( $id ) {
 340+ $id = intval( $id );
 341+ $this->failures = 0;
 342+ $ex = new MWException( "Graceful storage failure" );
 343+ while (true) {
 344+ if ( $this->spawn ) {
 345+ if ($this->failures) {
 346+ // we don't know why it failed, could be the child process
 347+ // borked, could be db entry busted, could be db server out to lunch,
 348+ // so cover all bases
480349 $this->closeSpawn();
481350 $this->openSpawn();
482351 }
483 - } catch (Exception $e) {
484 - $this->progress( "Rebooting getText infrastructure failed (".$e->getMessage().")" .
485 - " Trying to continue anyways" );
 352+ $text = $this->getTextSpawned( $id );
 353+ } else {
 354+ $text = $this->getTextDbSafe( $id );
486355 }
 356+ if ( $text === false ) {
 357+ $this->failures++;
 358+ if ( $this->failures > $this->maxFailures) {
 359+ $this->progress( "Failed to retrieve revision text for text id ".
 360+ "$id after $this->maxFailures tries, giving up" );
 361+ // were there so many bad retrievals in a row we want to bail?
 362+ // at some point we have to declare the dump irretrievably broken
 363+ $this->failedTextRetrievals++;
 364+ if ($this->failedTextRetrievals > $this->maxConsecutiveFailedTextRetrievals) {
 365+ throw $ex;
 366+ } else {
 367+ // would be nice to return something better to the caller someday,
 368+ // log what we know about the failure and about the revision
 369+ return "";
 370+ }
 371+ } else {
 372+ $this->progress( "Error $this->failures " .
 373+ "of allowed $this->maxFailures retrieving revision text for text id $id! " .
 374+ "Pausing $this->failureTimeout seconds before retry..." );
 375+ sleep( $this->failureTimeout );
 376+ }
 377+ } else {
 378+ $this->failedTextRetrievals= 0;
 379+ return $text;
 380+ }
487381 }
 382+ return '';
 383+ }
488384
489 - // Retirieving a good text for $id failed (at least) maxFailures times.
490 - // We abort for this $id.
491 -
492 - // Restoring the consecutive failures, and maybe aborting, if the dump
493 - // is too broken.
494 - $consecutiveFailedTextRetrievals = $oldConsecutiveFailedTextRetrievals + 1;
495 - if ( $consecutiveFailedTextRetrievals > $this->maxConsecutiveFailedTextRetrievals ) {
496 - throw new MWException( "Graceful storage failure" );
 385+ /**
 386+ * Fetch a text revision from the database, retrying in case of failure.
 387+ * This may survive some transitory errors by reconnecting, but
 388+ * may not survive a long-term server outage.
 389+ *
 390+ * FIXME: WTF? Why is it using a loop and then returning unconditionally?
 391+ * @param $id int
 392+ * @return bool|string
 393+ */
 394+ private function getTextDbSafe( $id ) {
 395+ while ( true ) {
 396+ try {
 397+ $text = $this->getTextDb( $id );
 398+ } catch ( DBQueryError $ex ) {
 399+ $text = false;
 400+ }
 401+ return $text;
497402 }
498 -
499 - return "";
500403 }
501404
502 -
503405 /**
504406 * May throw a database error if, say, the server dies during query.
505407 * @param $id
506408 * @return bool|string
507 - * @throws MWException
508409 */
509410 private function getTextDb( $id ) {
510411 global $wgContLang;
511 - if ( ! isset( $this->db ) ) {
512 - throw new MWException( __METHOD__ . "No database available" );
513 - }
514412 $row = $this->db->selectRow( 'text',
515413 array( 'old_text', 'old_flags' ),
516414 array( 'old_id' => $id ),
Index: trunk/phase3/includes/db/DatabasePostgres.php
@@ -708,19 +708,14 @@
709709 # Replace reserved words with better ones
710710 switch( $name ) {
711711 case 'user':
712 - return $this->realTableName( 'mwuser', $format );
 712+ return 'mwuser';
713713 case 'text':
714 - return $this->realTableName( 'pagecontent', $format );
 714+ return 'pagecontent';
715715 default:
716 - return $this->realTableName( $name, $format );
 716+ return parent::tableName( $name, $format );
717717 }
718718 }
719719
720 - /* Don't cheat on installer */
721 - function realTableName( $name, $format = 'quoted' ) {
722 - return parent::tableName( $name, $format );
723 - }
724 -
725720 /**
726721 * Return the next in a sequence, save the value for retrieval via insertId()
727722 * @return null
@@ -995,7 +990,7 @@
996991 if ( !$schema ) {
997992 $schema = $this->getCoreSchema();
998993 }
999 - $table = $this->realTableName( $table, 'raw' );
 994+ $table = $this->tableName( $table, 'raw' );
1000995 $etable = $this->addQuotes( $table );
1001996 $eschema = $this->addQuotes( $schema );
1002997 $SQL = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
Index: trunk/phase3/includes/db/Database.php
@@ -680,7 +680,7 @@
681681 $dbType = strtolower( $dbType );
682682 $class = 'Database' . ucfirst( $dbType );
683683
684 - if( in_array( $dbType, $canonicalDBTypes ) || ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) ) {
 684+ if( in_array( $dbType, $canonicalDBTypes ) ) {
685685 return new $class(
686686 isset( $p['host'] ) ? $p['host'] : false,
687687 isset( $p['user'] ) ? $p['user'] : false,
@@ -689,6 +689,8 @@
690690 isset( $p['flags'] ) ? $p['flags'] : 0,
691691 isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : 'get from global'
692692 );
 693+ } elseif ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) {
 694+ return new $class( $p );
693695 } else {
694696 return null;
695697 }
Index: trunk/phase3/includes/filerepo/file/LocalFile.php
@@ -908,13 +908,9 @@
909909 */
910910 function upload( $srcPath, $comment, $pageText, $flags = 0, $props = false, $timestamp = false, $user = null ) {
911911 global $wgContLang;
912 -
913 - if ( $this->getRepo()->getReadOnlyReason() !== false ) {
914 - return $this->readOnlyFatalStatus();
915 - }
916 -
917912 // truncate nicely or the DB will do it for us
918 - // non-nicely (dangling multi-byte chars, non-truncated version in cache).
 913+ // non-nicely (dangling multi-byte chars, non-truncated
 914+ // version in cache).
919915 $comment = $wgContLang->truncate( $comment, 255 );
920916 $this->lock(); // begin
921917 $status = $this->publish( $srcPath, $flags );
@@ -1179,10 +1175,6 @@
11801176 * archive name, or an empty string if it was a new file.
11811177 */
11821178 function publishTo( $srcPath, $dstRel, $flags = 0 ) {
1183 - if ( $this->getRepo()->getReadOnlyReason() !== false ) {
1184 - return $this->readOnlyFatalStatus();
1185 - }
1186 -
11871179 $this->lock(); // begin
11881180
11891181 $archiveName = wfTimestamp( TS_MW ) . '!'. $this->getName();
@@ -1219,10 +1211,6 @@
12201212 * @return FileRepoStatus object.
12211213 */
12221214 function move( $target ) {
1223 - if ( $this->getRepo()->getReadOnlyReason() !== false ) {
1224 - return $this->readOnlyFatalStatus();
1225 - }
1226 -
12271215 wfDebugLog( 'imagemove', "Got request to move {$this->name} to " . $target->getText() );
12281216 $this->lock(); // begin
12291217
@@ -1262,10 +1250,6 @@
12631251 * @return FileRepoStatus object.
12641252 */
12651253 function delete( $reason, $suppress = false ) {
1266 - if ( $this->getRepo()->getReadOnlyReason() !== false ) {
1267 - return $this->readOnlyFatalStatus();
1268 - }
1269 -
12701254 $this->lock(); // begin
12711255
12721256 $batch = new LocalFileDeleteBatch( $this, $reason, $suppress );
@@ -1282,7 +1266,7 @@
12831267 }
12841268 $status = $batch->execute();
12851269
1286 - if ( $status->isOK() ) {
 1270+ if ( $status->ok ) {
12871271 // Update site_stats
12881272 $site_stats = $dbw->tableName( 'site_stats' );
12891273 $dbw->query( "UPDATE $site_stats SET ss_images=ss_images-1", __METHOD__ );
@@ -1309,10 +1293,6 @@
13101294 * @return FileRepoStatus object.
13111295 */
13121296 function deleteOld( $archiveName, $reason, $suppress = false ) {
1313 - if ( $this->getRepo()->getReadOnlyReason() !== false ) {
1314 - return $this->readOnlyFatalStatus();
1315 - }
1316 -
13171297 $this->lock(); // begin
13181298
13191299 $batch = new LocalFileDeleteBatch( $this, $reason, $suppress );
@@ -1322,7 +1302,7 @@
13231303
13241304 $this->unlock(); // done
13251305
1326 - if ( $status->isOK() ) {
 1306+ if ( $status->ok ) {
13271307 $this->purgeDescription();
13281308 $this->purgeHistory();
13291309 }
@@ -1342,12 +1322,6 @@
13431323 * @return FileRepoStatus
13441324 */
13451325 function restore( $versions = array(), $unsuppress = false ) {
1346 - if ( $this->getRepo()->getReadOnlyReason() !== false ) {
1347 - return $this->readOnlyFatalStatus();
1348 - }
1349 -
1350 - $this->lock(); // begin
1351 -
13521326 $batch = new LocalFileRestoreBatch( $this, $unsuppress );
13531327
13541328 if ( !$versions ) {
@@ -1358,14 +1332,14 @@
13591333
13601334 $status = $batch->execute();
13611335
1362 - if ( $status->isGood() ) {
1363 - $cleanupStatus = $batch->cleanup();
1364 - $cleanupStatus->successCount = 0;
1365 - $cleanupStatus->failCount = 0;
1366 - $status->merge( $cleanupStatus );
 1336+ if ( !$status->isGood() ) {
 1337+ return $status;
13671338 }
13681339
1369 - $this->unlock(); // done
 1340+ $cleanupStatus = $batch->cleanup();
 1341+ $cleanupStatus->successCount = 0;
 1342+ $cleanupStatus->failCount = 0;
 1343+ $status->merge( $cleanupStatus );
13701344
13711345 return $status;
13721346 }
@@ -1470,14 +1444,6 @@
14711445 $dbw = $this->repo->getMasterDB();
14721446 $dbw->rollback( __METHOD__ );
14731447 }
1474 -
1475 - /**
1476 - * @return Status
1477 - */
1478 - protected function readOnlyFatalStatus() {
1479 - return $this->getRepo()->newFatal( 'filereadonlyerror', $this->getName(),
1480 - $this->getRepo()->getName(), $this->getRepo()->getReadOnlyReason() );
1481 - }
14821448 } // LocalFile class
14831449
14841450 # ------------------------------------------------------------------------------
@@ -1745,7 +1711,7 @@
17461712 $this->status->merge( $status );
17471713 }
17481714
1749 - if ( !$this->status->isOK() ) {
 1715+ if ( !$this->status->ok ) {
17501716 // Critical file deletion error
17511717 // Roll back inserts, release lock and abort
17521718 // TODO: delete the defunct filearchive rows if we are using a non-transactional DB
Index: trunk/phase3/includes/filerepo/file/File.php
@@ -760,27 +760,16 @@
761761 * @param $thumbUrl string Thumbnail URL
762762 * @param $params Array
763763 * @param $flags integer
764 - * @param $status Status Optional status object to use for errors
765764 * @return MediaTransformOutput
766765 */
767 - protected function transformErrorOutput(
768 - $thumbPath, $thumbUrl, $params, $flags, Status $status = null
769 - ) {
 766+ protected function transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ) {
770767 global $wgIgnoreImageErrors;
771768
772769 if ( $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) {
773770 return $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
774771 } else {
775 - $badStatus = Status::newFatal( 'thumbnail-dest-create' );
776 - if ( $status ) { // additional, more detailed errors
777 - $badStatus->merge( $status );
778 - }
779 - $err = array();
780 - foreach ( $badStatus->getErrorsArray() as $item ) {
781 - $err[] = wfMsg( $item[0], array_slice( $item, 1 ) );
782 - }
783772 return new MediaTransformError( 'thumbnail_error',
784 - $params['width'], 0, implode( "\n", $err ) ); // MTO does "\n" => "<br/>"
 773+ $params['width'], 0, wfMsg( 'thumbnail-dest-create' ) );
785774 }
786775 }
787776
@@ -886,8 +875,7 @@
887876 if ( $status->isOK() ) {
888877 $thumb->setStoragePath( $thumbPath );
889878 } else {
890 - $thumb = $this->transformErrorOutput(
891 - $thumbPath, $thumbUrl, $params, $flags, $status );
 879+ $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags );
892880 }
893881 }
894882
Index: trunk/phase3/includes/filerepo/backend/FileBackendMultiWrite.php
@@ -133,7 +133,7 @@
134134 }
135135
136136 // Actually attempt the operation batch...
137 - $subStatus = FileOp::attemptBatch( $performOps, $opts, $this->fileJournal );
 137+ $subStatus = FileOp::attemptBatch( $performOps, $opts );
138138
139139 $success = array();
140140 $failCount = 0;
Index: trunk/phase3/includes/filerepo/backend/FileOp.php
@@ -24,7 +24,6 @@
2525 protected $state = self::STATE_NEW; // integer
2626 protected $failed = false; // boolean
2727 protected $useLatest = true; // boolean
28 - protected $batchId; // string
2928
3029 protected $sourceSha1; // string
3130 protected $destSameAsSource; // boolean
@@ -64,16 +63,6 @@
6564 }
6665
6766 /**
68 - * Set the batch UUID this operation belongs to
69 - *
70 - * @param $batchId string
71 - * @return void
72 - */
73 - final protected function setBatchId( $batchId ) {
74 - $this->batchId = $batchId;
75 - }
76 -
77 - /**
7867 * Whether to allow stale data for file reads and stat checks
7968 *
8069 * @param $allowStale bool
@@ -84,57 +73,43 @@
8574 }
8675
8776 /**
88 - * Attempt to perform a series of file operations.
 77+ * Attempt a series of file operations.
8978 * Callers are responsible for handling file locking.
9079 *
9180 * $opts is an array of options, including:
92 - * 'force' : Errors that would normally cause a rollback do not.
93 - * The remaining operations are still attempted if any fail.
94 - * 'allowStale' : Don't require the latest available data.
95 - * This can increase performance for non-critical writes.
96 - * This has no effect unless the 'force' flag is set.
97 - * 'nonJournaled' : Don't log this operation batch in the file journal.
98 - *
 81+ * 'force' : Errors that would normally cause a rollback do not.
 82+ * The remaining operations are still attempted if any fail.
 83+ * 'allowStale' : Don't require the latest available data.
 84+ * This can increase performance for non-critical writes.
 85+ * This has no effect unless the 'force' flag is set.
 86+ *
9987 * The resulting Status will be "OK" unless:
10088 * a) unexpected operation errors occurred (network partitions, disk full...)
10189 * b) significant operation errors occured and 'force' was not set
10290 *
10391 * @param $performOps Array List of FileOp operations
10492 * @param $opts Array Batch operation options
105 - * @param $journal FileJournal Journal to log operations to
10693 * @return Status
10794 */
108 - final public static function attemptBatch(
109 - array $performOps, array $opts, FileJournal $journal
110 - ) {
 95+ final public static function attemptBatch( array $performOps, array $opts ) {
11196 $status = Status::newGood();
11297
 98+ $allowStale = !empty( $opts['allowStale'] );
 99+ $ignoreErrors = !empty( $opts['force'] );
 100+
113101 $n = count( $performOps );
114102 if ( $n > self::MAX_BATCH_SIZE ) {
115103 $status->fatal( 'backend-fail-batchsize', $n, self::MAX_BATCH_SIZE );
116104 return $status;
117105 }
118106
119 - $batchId = $journal->getTimestampedUUID();
120 - $allowStale = !empty( $opts['allowStale'] );
121 - $ignoreErrors = !empty( $opts['force'] );
122 - $journaled = empty( $opts['nonJournaled'] );
123 -
124 - $entries = array(); // file journal entries
125107 $predicates = FileOp::newPredicates(); // account for previous op in prechecks
126108 // Do pre-checks for each operation; abort on failure...
127109 foreach ( $performOps as $index => $fileOp ) {
128 - $fileOp->setBatchId( $batchId );
129110 $fileOp->allowStaleReads( $allowStale );
130 - $oldPredicates = $predicates;
131 - $subStatus = $fileOp->precheck( $predicates ); // updates $predicates
 111+ $subStatus = $fileOp->precheck( $predicates );
132112 $status->merge( $subStatus );
133 - if ( $subStatus->isOK() ) {
134 - if ( $journaled ) { // journal log entry
135 - $entries = array_merge( $entries,
136 - self::getJournalEntries( $fileOp, $oldPredicates, $predicates ) );
137 - }
138 - } else { // operation failed?
 113+ if ( !$subStatus->isOK() ) { // operation failed?
139114 $status->success[$index] = false;
140115 ++$status->failCount;
141116 if ( !$ignoreErrors ) {
@@ -143,15 +118,8 @@
144119 }
145120 }
146121
147 - // Log the operations in file journal...
148 - if ( count( $entries ) ) {
149 - $subStatus = $journal->logChangeBatch( $entries, $batchId );
150 - if ( !$subStatus->isOK() ) {
151 - return $subStatus; // abort
152 - }
153 - }
154 -
155 - if ( $ignoreErrors ) { // treat precheck() fatals as mere warnings
 122+ if ( $ignoreErrors ) {
 123+ # Treat all precheck() fatals as merely warnings
156124 $status->setResult( true, $status->value );
157125 }
158126
@@ -187,46 +155,6 @@
188156 }
189157
190158 /**
191 - * Get the file journal entries for a single file operation
192 - *
193 - * @param $fileOp FileOp
194 - * @param $oPredicates Array Pre-op information about files
195 - * @param $nPredicates Array Post-op information about files
196 - * @return Array
197 - */
198 - final protected static function getJournalEntries(
199 - FileOp $fileOp, array $oPredicates, array $nPredicates
200 - ) {
201 - $nullEntries = array();
202 - $updateEntries = array();
203 - $deleteEntries = array();
204 - $pathsUsed = array_merge( $fileOp->storagePathsRead(), $fileOp->storagePathsChanged() );
205 - foreach ( $pathsUsed as $path ) {
206 - $nullEntries[] = array( // assertion for recovery
207 - 'op' => 'null',
208 - 'path' => $path,
209 - 'newSha1' => $fileOp->fileSha1( $path, $oPredicates )
210 - );
211 - }
212 - foreach ( $fileOp->storagePathsChanged() as $path ) {
213 - if ( $nPredicates['sha1'][$path] === false ) { // deleted
214 - $deleteEntries[] = array(
215 - 'op' => 'delete',
216 - 'path' => $path,
217 - 'newSha1' => ''
218 - );
219 - } else { // created/updated
220 - $updateEntries[] = array(
221 - 'op' => $fileOp->fileExists( $path, $oPredicates ) ? 'update' : 'create',
222 - 'path' => $path,
223 - 'newSha1' => $nPredicates['sha1'][$path]
224 - );
225 - }
226 - }
227 - return array_merge( $nullEntries, $updateEntries, $deleteEntries );
228 - }
229 -
230 - /**
231159 * Get the value of the parameter with the given name
232160 *
233161 * @param $name string
@@ -424,8 +352,8 @@
425353 $params = $this->params;
426354 $params['failedAction'] = $action;
427355 try {
428 - wfDebugLog( 'FileOperation', get_class( $this ) .
429 - " failed (batch #{$this->batchId}): " . FormatJson::encode( $params ) );
 356+ wfDebugLog( 'FileOperation',
 357+ get_class( $this ) . ' failed: ' . FormatJson::encode( $params ) );
430358 } catch ( Exception $e ) {
431359 // bad config? debug log error?
432360 }
Index: trunk/phase3/includes/filerepo/backend/FileBackendStore.php
@@ -708,7 +708,7 @@
709709 $this->clearCache();
710710
711711 // Actually attempt the operation batch...
712 - $subStatus = FileOp::attemptBatch( $performOps, $opts, $this->fileJournal );
 712+ $subStatus = FileOp::attemptBatch( $performOps, $opts );
713713
714714 // Merge errors into status fields
715715 $status->merge( $subStatus );
Index: trunk/phase3/includes/filerepo/backend/FileBackend.php
@@ -45,8 +45,6 @@
4646 protected $readOnly; // string; read-only explanation message
4747 /** @var LockManager */
4848 protected $lockManager;
49 - /** @var FileJournal */
50 - protected $fileJournal;
5149
5250 /**
5351 * Create a new backend instance from configuration.
@@ -57,10 +55,8 @@
5856 * This should consist of alphanumberic, '-', and '_' characters.
5957 * This name should not be changed after use.
6058 * 'wikiId' : Prefix to container names that is unique to this wiki.
61 - * It should only consist of alphanumberic, '-', and '_' characters.
 59+ * This should consist of alphanumberic, '-', and '_' characters.
6260 * 'lockManager' : Registered name of a file lock manager to use.
63 - * 'fileJournal' : File journal configuration; see FileJournal::factory().
64 - * Journals simply log changes to files stored in the backend.
6561 * 'readOnly' : Write operations are disallowed if this is a non-empty string.
6662 * It should be an explanation for the backend being read-only.
6763 *
@@ -77,9 +73,6 @@
7874 $this->lockManager = ( $config['lockManager'] instanceof LockManager )
7975 ? $config['lockManager']
8076 : LockManagerGroup::singleton()->get( $config['lockManager'] );
81 - $this->fileJournal = isset( $config['fileJournal'] )
82 - ? FileJournal::factory( $config['fileJournal'], $this->name )
83 - : FileJournal::factory( array( 'class' => 'NullFileJournal' ), $this->name );
8477 $this->readOnly = isset( $config['readOnly'] )
8578 ? (string)$config['readOnly']
8679 : '';
@@ -184,8 +177,6 @@
185178 * 'allowStale' : Don't require the latest available data.
186179 * This can increase performance for non-critical writes.
187180 * This has no effect unless the 'force' flag is set.
188 - * 'nonJournaled' : Don't log this operation batch in the file journal.
189 - * This limits the ability of recovery scripts.
190181 *
191182 * Remarks on locking:
192183 * File system paths given to operations should refer to files that are
Index: trunk/phase3/includes/filerepo/FileRepo.php
@@ -120,15 +120,6 @@
121121 }
122122
123123 /**
124 - * Get an explanatory message if this repo is read-only
125 - *
126 - * @return string|bool Returns false if the repo is not read-only
127 - */
128 - public function getReadOnlyReason() {
129 - return $this->backend->getReadOnlyReason();
130 - }
131 -
132 - /**
133124 * Prepare a single zone or list of zones for usage.
134125 * See initDeletedDir() for additional setup needed for the 'deleted' zone.
135126 *
Index: trunk/phase3/includes/installer/PostgresUpdater.php
@@ -27,11 +27,6 @@
2828 */
2929 protected function getCoreUpdateList() {
3030 return array(
31 - # rename tables 1.7.3
32 - # r15791 Change reserved word table names "user" and "text"
33 - array( 'renameTable', 'user', 'mwuser'),
34 - array( 'renameTable', 'text', 'pagecontent'),
35 -
3631 # new sequences
3732 array( 'addSequence', 'logging_log_id_seq' ),
3833 array( 'addSequence', 'page_restrictions_pr_id_seq' ),
@@ -411,8 +406,7 @@
412407 protected function renameTable( $old, $new ) {
413408 if ( $this->db->tableExists( $old ) ) {
414409 $this->output( "Renaming table $old to $new\n" );
415 - $old = $this->db->realTableName( $old, "quoted" );
416 - $new = $this->db->realTableName( $new, "quoted" );
 410+ $old = $this->db->addQuotes( $old );
417411 $this->db->query( "ALTER TABLE $old RENAME TO $new" );
418412 }
419413 }
Index: trunk/phase3/includes/installer/DatabaseInstaller.php
@@ -158,7 +158,7 @@
159159 }
160160 $this->db->selectDB( $this->getVar( 'wgDBname' ) );
161161
162 - if( $this->db->tableExists( 'archive', __METHOD__ ) ) {
 162+ if( $this->db->tableExists( 'user', __METHOD__ ) ) {
163163 $status->warning( 'config-install-tables-exist' );
164164 $this->enableLB();
165165 return $status;
Index: trunk/phase3/includes/api/ApiFeedContributions.php
@@ -43,80 +43,56 @@
4444 }
4545
4646 public function execute() {
 47+ $params = $this->extractRequestParams();
4748
4849 global $wgFeed, $wgFeedClasses, $wgSitename, $wgLanguageCode;
4950
50 - try {
51 - $params = $this->extractRequestParams();
52 -
53 - if( !$wgFeed ) {
54 - $this->dieUsage( 'Syndication feeds are not available', 'feed-unavailable' );
55 - }
56 -
57 - if( !isset( $wgFeedClasses[ $params['feedformat'] ] ) ) {
58 - $this->dieUsage( 'Invalid subscription feed type', 'feed-invalid' );
59 - }
 51+ if( !$wgFeed ) {
 52+ $this->dieUsage( 'Syndication feeds are not available', 'feed-unavailable' );
 53+ }
6054
61 - global $wgMiserMode;
62 - if ( $params['showsizediff'] && $wgMiserMode ) {
63 - $this->dieUsage( 'Size difference is disabled in Miser Mode', 'sizediffdisabled' );
64 - }
65 -
66 - $msg = wfMsgForContent( 'Contributions' );
67 - $feedTitle = $wgSitename . ' - ' . $msg . ' [' . $wgLanguageCode . ']';
68 - $feedUrl = SpecialPage::getTitleFor( 'Contributions', $params['user'] )->getFullURL();
69 -
70 - $target = $params['user'] == 'newbies'
71 - ? 'newbies'
72 - : Title::makeTitleSafe( NS_USER, $params['user'] )->getText();
73 -
74 - $feed = new $wgFeedClasses[$params['feedformat']] (
75 - $feedTitle,
76 - htmlspecialchars( $msg ),
77 - $feedUrl
78 - );
79 -
80 - $pager = new ContribsPager( $this->getContext(), array(
81 - 'target' => $target,
82 - 'namespace' => $params['namespace'],
83 - 'year' => $params['year'],
84 - 'month' => $params['month'],
85 - 'tagFilter' => $params['tagfilter'],
86 - 'deletedOnly' => $params['deletedonly'],
87 - 'topOnly' => $params['toponly'],
88 - 'showSizeDiff' => $params['showsizediff'],
89 - ) );
90 -
91 - $feedItems = array();
92 - if( $pager->getNumRows() > 0 ) {
93 - foreach ( $pager->mResult as $row ) {
94 - $feedItems[] = $this->feedItem( $row );
95 - }
96 - }
97 -
98 - ApiFormatFeedWrapper::setResult( $this->getResult(), $feed, $feedItems );
99 -
100 - } catch ( Exception $e ) {
101 - // Error results should not be cached
102 - $this->getMain()->setCacheMaxAge( 0 );
 55+ if( !isset( $wgFeedClasses[ $params['feedformat'] ] ) ) {
 56+ $this->dieUsage( 'Invalid subscription feed type', 'feed-invalid' );
 57+ }
10358
104 - $feedTitle = $wgSitename . ' - Error - ' . wfMsgForContent( 'contributions' ) . ' [' . $wgLanguageCode . ']';
105 - $feedUrl = SpecialPage::getTitleFor( 'Contributions', $params['user'] )->getFullURL();
 59+ global $wgMiserMode;
 60+ if ( $params['showsizediff'] && $wgMiserMode ) {
 61+ $this->dieUsage( 'Size difference is disabled in Miser Mode', 'sizediffdisabled' );
 62+ }
10663
107 - $feedFormat = isset( $params['feedformat'] ) ? $params['feedformat'] : 'rss';
108 - $feed = new $wgFeedClasses[$feedFormat] ( $feedTitle, htmlspecialchars( wfMsgForContent( 'contributions' ) ), $feedUrl );
 64+ $msg = wfMsgForContent( 'Contributions' );
 65+ $feedTitle = $wgSitename . ' - ' . $msg . ' [' . $wgLanguageCode . ']';
 66+ $feedUrl = SpecialPage::getTitleFor( 'Contributions', $params['user'] )->getFullURL();
10967
110 - if ( $e instanceof UsageException ) {
111 - $errorCode = $e->getCodeString();
112 - } else {
113 - // Something is seriously wrong
114 - $errorCode = 'internal_api_error';
115 - }
 68+ $target = $params['user'] == 'newbies'
 69+ ? 'newbies'
 70+ : Title::makeTitleSafe( NS_USER, $params['user'] )->getText();
11671
117 - $errorText = $e->getMessage();
118 - $feedItems[] = new FeedItem( "Error ($errorCode)", $errorText, '', '', '' );
119 - ApiFormatFeedWrapper::setResult( $this->getResult(), $feed, $feedItems );
 72+ $feed = new $wgFeedClasses[$params['feedformat']] (
 73+ $feedTitle,
 74+ htmlspecialchars( $msg ),
 75+ $feedUrl
 76+ );
 77+
 78+ $pager = new ContribsPager( $this->getContext(), array(
 79+ 'target' => $target,
 80+ 'namespace' => $params['namespace'],
 81+ 'year' => $params['year'],
 82+ 'month' => $params['month'],
 83+ 'tagFilter' => $params['tagfilter'],
 84+ 'deletedOnly' => $params['deletedonly'],
 85+ 'topOnly' => $params['toponly'],
 86+ 'showSizeDiff' => $params['showsizediff'],
 87+ ) );
 88+
 89+ $feedItems = array();
 90+ if( $pager->getNumRows() > 0 ) {
 91+ foreach ( $pager->mResult as $row ) {
 92+ $feedItems[] = $this->feedItem( $row );
 93+ }
12094 }
 95+
 96+ ApiFormatFeedWrapper::setResult( $this->getResult(), $feed, $feedItems );
12197 }
12298
12399 protected function feedItem( $row ) {
Index: trunk/phase3/includes/AutoLoader.php
@@ -508,9 +508,6 @@
509509 'FSFileBackendFileList' => 'includes/filerepo/backend/FSFileBackend.php',
510510 'SwiftFileBackend' => 'includes/filerepo/backend/SwiftFileBackend.php',
511511 'SwiftFileBackendFileList' => 'includes/filerepo/backend/SwiftFileBackend.php',
512 - 'FileJournal' => 'includes/filerepo/backend/filejournal/FileJournal.php',
513 - 'DBFileJournal' => 'includes/filerepo/backend/filejournal/DBFileJournal.php',
514 - 'NullFileJournal' => 'includes/filerepo/backend/filejournal/FileJournal.php',
515512 'LockManagerGroup' => 'includes/filerepo/backend/lockmanager/LockManagerGroup.php',
516513 'LockManager' => 'includes/filerepo/backend/lockmanager/LockManager.php',
517514 'ScopedLock' => 'includes/filerepo/backend/lockmanager/LockManager.php',
Index: trunk/phase3/includes/specials/SpecialBlock.php
@@ -381,19 +381,19 @@
382382 $this->getLanguage()->pipeList( $links )
383383 );
384384
385 - $userTitle = self::getTargetUserTitle( $this->target );
386 - if( $userTitle ){
 385+ if( $this->target instanceof User ){
387386 # Get relevant extracts from the block and suppression logs, if possible
 387+ $userpage = $this->target->getUserPage();
388388 $out = '';
389389
390390 LogEventsList::showLogExtract(
391391 $out,
392392 'block',
393 - $userTitle,
 393+ $userpage,
394394 '',
395395 array(
396396 'lim' => 10,
397 - 'msgKey' => array( 'blocklog-showlog', $userTitle->getText() ),
 397+ 'msgKey' => array( 'blocklog-showlog', $userpage->getText() ),
398398 'showIfEmpty' => false
399399 )
400400 );
@@ -404,12 +404,12 @@
405405 LogEventsList::showLogExtract(
406406 $out,
407407 'suppress',
408 - $userTitle,
 408+ $userpage,
409409 '',
410410 array(
411411 'lim' => 10,
412412 'conds' => array( 'log_action' => array( 'block', 'reblock', 'unblock' ) ),
413 - 'msgKey' => array( 'blocklog-showsuppresslog', $userTitle->getText() ),
 413+ 'msgKey' => array( 'blocklog-showsuppresslog', $userpage->getText() ),
414414 'showIfEmpty' => false
415415 )
416416 );
@@ -422,21 +422,6 @@
423423 }
424424
425425 /**
426 - * Get a user page target for things like logs.
427 - * This handles account and IP range targets.
428 - * @param $target User|string
429 - * @return Title|null
430 - */
431 - protected static function getTargetUserTitle( $target ) {
432 - if( $target instanceof User ) {
433 - return $target->getUserPage();
434 - } elseif ( IP::isIPAddress( $target ) ) {
435 - return Title::makeTitleSafe( NS_USER, $target );
436 - }
437 - return null;
438 - }
439 -
440 - /**
441426 * Determine the target of the block, and the type of target
442427 * TODO: should be in Block.php?
443428 * @param $par String subpage parameter passed to setup, or data value from
Index: trunk/phase3/languages/messages/MessagesFrp.php
@@ -26,8 +26,8 @@
2727 NS_MEDIA => 'Mèdia',
2828 NS_SPECIAL => 'Spèciâl',
2929 NS_TALK => 'Discussion',
30 - NS_USER => 'Usanciér',
31 - NS_USER_TALK => 'Discussion_usanciér',
 30+ NS_USER => 'Utilisator',
 31+ NS_USER_TALK => 'Discussion_utilisator',
3232 NS_PROJECT_TALK => 'Discussion_$1',
3333 NS_FILE => 'Fichiér',
3434 NS_FILE_TALK => 'Discussion_fichiér',
@@ -42,14 +42,13 @@
4343 );
4444
4545 $namespaceAliases = array(
46 - 'Discutar' => NS_TALK,
47 - 'Utilisator' => NS_USER,
 46+ 'Discutar' => NS_TALK,
4847 'Discussion_Utilisator' => NS_USER_TALK,
49 - 'Émâge' => NS_FILE,
50 - 'Discussion_Émâge' => NS_FILE_TALK,
51 - 'Discussion_Modèlo' => NS_TEMPLATE_TALK,
52 - 'Discussion_Éde' => NS_HELP_TALK,
53 - 'Discussion_Catègorie' => NS_CATEGORY_TALK
 48+ 'Émâge' => NS_FILE,
 49+ 'Discussion_Émâge' => NS_FILE_TALK,
 50+ 'Discussion_Modèlo' => NS_TEMPLATE_TALK,
 51+ 'Discussion_Éde' => NS_HELP_TALK,
 52+ 'Discussion_Catègorie' => NS_CATEGORY_TALK
5453 );
5554
5655 $specialPageAliases = array(
Index: trunk/phase3/languages/messages/MessagesMk.php
@@ -23,7 +23,7 @@
2424 */
2525
2626 $namespaceNames = array(
27 - NS_MEDIA => 'Медиум',
 27+ NS_MEDIA => 'Медија',
2828 NS_SPECIAL => 'Специјална',
2929 NS_TALK => 'Разговор',
3030 NS_USER => 'Корисник',
@@ -42,9 +42,8 @@
4343 );
4444
4545 $namespaceAliases = array(
46 - 'Медија' => NS_MEDIA,
47 - 'Специјални' => NS_SPECIAL,
48 - 'Слика' => NS_FILE,
 46+ 'Специјални' => NS_SPECIAL,
 47+ 'Слика' => NS_FILE,
4948 'Разговор_за_слика' => NS_FILE_TALK,
5049 );
5150
Index: trunk/phase3/languages/messages/MessagesYue.php
@@ -22,11 +22,11 @@
2323 NS_TALK => '傾偈',
2424 NS_USER => '用戶',
2525 NS_USER_TALK => '用戶傾偈',
26 - NS_PROJECT_TALK => '$1傾偈',
 26+ NS_PROJECT_TALK => '$1_傾偈',
2727 NS_FILE => '文件',
2828 NS_FILE_TALK => '文件傾偈',
2929 NS_MEDIAWIKI => 'MediaWiki',
30 - NS_MEDIAWIKI_TALK => 'MediaWiki傾偈',
 30+ NS_MEDIAWIKI_TALK => 'MediaWiki_傾偈',
3131 NS_TEMPLATE => '模',
3232 NS_TEMPLATE_TALK => '模傾偈',
3333 NS_HELP => '幫手',
@@ -47,7 +47,9 @@
4848 "用户 对话" => NS_USER_TALK,
4949 "用戶 討論" => NS_USER_TALK,
5050 "用户 讨论" => NS_USER_TALK,
51 - '$1_傾偈' => NS_PROJECT_TALK,
 51+ # This has never worked so it's unlikely to annoy anyone if I disable it -- TS
 52+ # "{$wgMetaNamespace} 討論" => NS_PROJECT_TALK,
 53+ # "{$wgMetaNamespace} 讨论" => NS_PROJECT_TALK,
5254 "檔" => NS_FILE,
5355 "檔案" => NS_FILE,
5456 "档" => NS_FILE,
@@ -66,7 +68,6 @@
6769 "图 讨论" => NS_FILE_TALK,
6870 "圖像 討論" => NS_FILE_TALK,
6971 "图像 讨论" => NS_FILE_TALK,
70 - 'MediaWiki_傾偈' => NS_FILE_TALK,
7172 "模 討論" => NS_TEMPLATE_TALK,
7273 "模 讨论" => NS_TEMPLATE_TALK,
7374 "幫助" => NS_HELP,
Index: trunk/phase3/languages/messages/MessagesNds_nl.php
@@ -23,35 +23,30 @@
2424
2525 $namespaceNames = array(
2626 NS_MEDIA => 'Media',
27 - NS_SPECIAL => 'Spesiaal',
 27+ NS_SPECIAL => 'Speciaal',
2828 NS_TALK => 'Overleg',
2929 NS_USER => 'Gebruker',
3030 NS_USER_TALK => 'Overleg_gebruker',
3131 NS_PROJECT_TALK => 'Overleg_$1',
32 - NS_FILE => 'Bestaand',
33 - NS_FILE_TALK => 'Overleg_bestaand',
 32+ NS_FILE => 'Ofbeelding',
 33+ NS_FILE_TALK => 'Overleg_ofbeelding',
3434 NS_MEDIAWIKI => 'MediaWiki',
3535 NS_MEDIAWIKI_TALK => 'Overleg_MediaWiki',
3636 NS_TEMPLATE => 'Mal',
3737 NS_TEMPLATE_TALK => 'Overleg_mal',
3838 NS_HELP => 'Hulpe',
3939 NS_HELP_TALK => 'Overleg_hulpe',
40 - NS_CATEGORY => 'Kategorie',
41 - NS_CATEGORY_TALK => 'Overleg_kategorie',
 40+ NS_CATEGORY => 'Kattegerie',
 41+ NS_CATEGORY_TALK => 'Overleg_kattegerie',
4242 );
4343
4444 $namespaceAliases = array(
45 - 'Speciaol' => NS_SPECIAL,
46 - 'Speciaal' => NS_SPECIAL,
47 - 'Sjabloon' => NS_TEMPLATE,
48 - 'Overleg_sjabloon' => NS_TEMPLATE_TALK,
49 - 'Ofbeelding' => NS_FILE,
50 - 'Overleg_ofbeelding' => NS_FILE_TALK,
51 - 'Categorie' => NS_CATEGORY,
52 - 'Overleg_categorie' => NS_CATEGORY_TALK,
53 - 'Kattegerie' => NS_CATEGORY,
54 - 'Overleg_categorie' => NS_CATEGORY_TALK,
55 - 'Overleg_kattegerie' => NS_HELP_TALK,
 45+ 'Speciaol' => NS_SPECIAL,
 46+ 'Sjabloon' => NS_TEMPLATE,
 47+ 'Overleg_sjabloon' => NS_TEMPLATE_TALK,
 48+ 'Categorie' => NS_CATEGORY,
 49+ 'Overleg_categorie' => NS_CATEGORY_TALK,
 50+ 'Overleg_help' => NS_HELP_TALK,
5651 );
5752
5853 $dateFormats = array(
Index: trunk/phase3/languages/messages/MessagesEn.php
@@ -2277,9 +2277,6 @@
22782278 'backend-fail-contenttype' => 'Could not determine the content type of the file to store at "$1".',
22792279 'backend-fail-batchsize' => 'Storage backend given a batch of $1 file {{PLURAL:$1|operation|operations}}; the limit is $2 {{PLURAL:$2|operation|operations}}.',
22802280
2281 -'filejournal-fail-dbconnect' => 'Could not connect to the journal database for storage backend "$1".',
2282 -'filejournal-fail-dbquery' => 'Could not update the journal database for storage backend "$1".',
2283 -
22842281 # Lock manager
22852282 'lockmanager-notlocked' => 'Could not unlock "$1"; it is not locked.',
22862283 'lockmanager-fail-closelock' => 'Could not close lock file for "$1".',
Index: trunk/phase3/languages/messages/MessagesWar.php
@@ -14,25 +14,6 @@
1515 * @author לערי ריינהארט
1616 */
1717
18 -$namespaceNames = array(
19 - NS_MEDIA => 'Medya',
20 - NS_SPECIAL => 'Pinaurog',
21 - NS_TALK => 'Hiruhimangraw',
22 - NS_USER => 'Gumaramit',
23 - NS_USER_TALK => 'Hiruhimangaw_hiton_gumaramit',
24 - NS_PROJECT_TALK => 'Hiruhimangraw_hiton_$1',
25 - NS_FILE => 'Fayl',
26 - NS_FILE_TALK => 'Hiruhimangraw_hiton_fayl',
27 - NS_MEDIAWIKI => 'MediaWiki',
28 - NS_MEDIAWIKI_TALK => 'Hiruhimangraw_hiton_MediaWiki',
29 - NS_TEMPLATE => 'Batakan',
30 - NS_TEMPLATE_TALK => 'Hiruhimangraw_hiton_batakan',
31 - NS_HELP => 'Bulig',
32 - NS_HELP_TALK => 'Hiruhimangaw_hiton_bulig',
33 - NS_CATEGORY => 'Kaarangay',
34 - NS_CATEGORY_TALK => 'Hiruhimangraw_hiton_kaarangay',
35 -);
36 -
3718 $specialPageAliases = array(
3819 'Allpages' => array( 'NgatananngaPakli' ),
3920 'Categories' => array( 'Mga_kaarangay' ),
Index: trunk/phase3/languages/messages/MessagesSa.php
@@ -44,7 +44,7 @@
4545
4646 $namespaceNames = array(
4747 NS_MEDIA => 'माध्यमम्',
48 - NS_SPECIAL => 'विशेषम्',
 48+ NS_SPECIAL => 'विशेष',
4949 NS_TALK => 'सम्भाषणम्',
5050 NS_USER => 'योजकः',
5151 NS_USER_TALK => 'योजकसम्भाषणम्',
@@ -63,7 +63,6 @@
6464
6565 $namespaceAliases = array(
6666 'माध्यम' => NS_MEDIA,
67 - 'विशेष' => NS_SPECIAL,
6867 'संभाषणं' => NS_TALK,
6968 'योजकसंभाषणं' => NS_USER_TALK,
7069 '$1संभाषणं' => NS_PROJECT_TALK,
Index: trunk/phase3/languages/messages/MessagesOr.php
@@ -40,17 +40,17 @@
4141 NS_MEDIA => 'ମାଧ୍ୟମ',
4242 NS_SPECIAL => 'ବିଶେଷ',
4343 NS_TALK => 'ଆଲୋଚନା',
44 - NS_USER => 'ବ୍ୟବହାରକାରୀ',
45 - NS_USER_TALK => 'ବ୍ୟବହାରକାରୀଙ୍କ_ଆଲୋଚନା',
46 - NS_PROJECT_TALK => '$1_ଆଲୋଚନା',
 44+ NS_USER => 'ବ୍ୟବାହାରକାରୀ',
 45+ NS_USER_TALK => 'ବ୍ୟବାହାରକାରୀଙ୍କ_ଆଲୋଚନା',
 46+ NS_PROJECT_TALK => 'ଉଇକିପିଡ଼ିଆ_ଆଲୋଚନା',
4747 NS_FILE => 'ଫାଇଲ',
4848 NS_FILE_TALK => 'ଫାଇଲ_ଆଲୋଚନା',
4949 NS_MEDIAWIKI => 'ମିଡ଼ିଆଉଇକି',
5050 NS_MEDIAWIKI_TALK => 'ମିଡ଼ିଆଉଇକି_ଆଲୋଚନା',
51 - NS_TEMPLATE => 'ଛାଞ୍ଚ',
52 - NS_TEMPLATE_TALK => 'ଛାଞ୍ଚ_ଆଲୋଚନା',
53 - NS_HELP => 'ସହଯୋଗ',
54 - NS_HELP_TALK => 'ସହଯୋଗ_ଆଲୋଚନା',
 51+ NS_TEMPLATE => 'ଟେମ୍ପଲେଟ',
 52+ NS_TEMPLATE_TALK => 'ଟେମ୍ପଲେଟ_ଆଲୋଚନା',
 53+ NS_HELP => 'ସାହାଯ୍ୟ',
 54+ NS_HELP_TALK => 'ସାହାଯ୍ୟ_ଆଲୋଚନା',
5555 NS_CATEGORY => 'ଶ୍ରେଣୀ',
5656 NS_CATEGORY_TALK => 'ଶ୍ରେଣୀ_ଆଲୋଚନା',
5757 );
@@ -58,17 +58,10 @@
5959 $namespaceAliases = array(
6060 'ବ୍ୟବହାରକାରି' => NS_USER,
6161 'ବ୍ୟବହାରକାରିଁକ_ଆଲୋଚନା' => NS_USER_TALK,
62 - 'ବ୍ୟବାହାରକାରୀ' => NS_USER,
63 - 'ବ୍ୟବାହାରକାରୀଙ୍କ_ଆଲୋଚନା' => NS_USER_TALK,
64 - 'ଉଇକିପିଡ଼ିଆ_ଆଲୋଚନା' => NS_PROJECT_TALK,
6562 'ଟେଁପଲେଟ' => NS_TEMPLATE,
6663 'ଟେଁପଲେଟ_ଆଲୋଚନା' => NS_TEMPLATE_TALK,
67 - 'ଟେମ୍ପଲେଟ' => NS_TEMPLATE,
68 - 'ଟେମ୍ପଲେଟ_ଆଲୋଚନା' => NS_TEMPLATE_TALK,
6964 'ବିଭାଗ' => NS_CATEGORY,
7065 'ବିଭାଗିୟ_ଆଲୋଚନା' => NS_CATEGORY_TALK,
71 - 'ସାହାଯ୍ୟ' => NS_HELP,
72 - 'ସାହାଯ୍ୟ_ଆଲୋଚନା' => NS_HELP_TALK,
7366 );
7467
7568 $specialPageAliases = array(
Index: trunk/phase3/languages/messages/MessagesMzn.php
@@ -24,52 +24,46 @@
2525 $rtl = true;
2626
2727 $namespaceNames = array(
28 - NS_MEDIA => 'مدیا',
 28+ NS_MEDIA => 'مه‌دیا',
2929 NS_SPECIAL => 'شا',
30 - NS_MAIN => '',
3130 NS_TALK => 'گپ',
3231 NS_USER => 'کارور',
3332 NS_USER_TALK => 'کارور_گپ',
3433 NS_PROJECT_TALK => '$1_گپ',
3534 NS_FILE => 'پرونده',
3635 NS_FILE_TALK => 'پرونده_گپ',
37 - NS_MEDIAWIKI => 'مدیاویکی',
38 - NS_MEDIAWIKI_TALK => 'مدیاویکی_گپ',
 36+ NS_MEDIAWIKI => 'مه‌دیاویکی',
 37+ NS_MEDIAWIKI_TALK => 'مه‌دیاویکی_گپ',
3938 NS_TEMPLATE => 'شابلون',
4039 NS_TEMPLATE_TALK => 'شابلون_گپ',
41 - NS_HELP => 'رانما',
42 - NS_HELP_TALK => 'رانما_گپ',
 40+ NS_HELP => 'رانه‌ما',
 41+ NS_HELP_TALK => 'رانه‌مائه_گپ',
4342 NS_CATEGORY => 'رج',
4443 NS_CATEGORY_TALK => 'رج_گپ',
4544 );
4645
4746 $namespaceAliases = array(
48 - 'مه‌دیا' => NS_MEDIA,
4947 'مدیا' => NS_MEDIA,
5048 'ویژه' => NS_SPECIAL,
51 - 'بحث' => NS_TALK,
 49+ 'بحث' => NS_TALK,
5250 'کاربر' => NS_USER,
53 - 'بحث_کاربر' => NS_USER_TALK,
54 - 'بحث_$1' => NS_PROJECT_TALK,
 51+ 'بحث_کاربر' => NS_USER_TALK,
 52+ 'بحث_$1' => NS_PROJECT_TALK,
5553 'تصویر' => NS_FILE,
5654 'پرونده' => NS_FILE,
57 - 'بحث_تصویر' => NS_FILE_TALK,
58 - 'بحث_پرونده' => NS_FILE_TALK,
 55+ 'بحث_تصویر' => NS_FILE_TALK,
 56+ 'بحث_پرونده' => NS_FILE_TALK,
5957 'مدیاویکی' => NS_MEDIAWIKI,
6058 'مه‌دیا ویکی' => NS_MEDIAWIKI,
61 - 'مه‌دیاویکی' => NS_MEDIAWIKI,
62 - 'مه‌دیاویکی_گپ' => NS_MEDIAWIKI_TALK,
63 - 'بحث_مدیاویکی' => NS_MEDIAWIKI_TALK,
 59+ 'بحث_مدیاویکی' => NS_MEDIAWIKI_TALK,
6460 'مه‌دیا ویکی گپ' => NS_MEDIAWIKI_TALK,
6561 'الگو' => NS_TEMPLATE,
66 - 'بحث_الگو' => NS_TEMPLATE_TALK,
 62+ 'بحث_الگو' => NS_TEMPLATE_TALK,
6763 'راهنما' => NS_HELP,
68 - 'رانه‌ما' => NS_HELP,
69 - 'رانه‌مائه_گپ' => NS_HELP_TALK,
70 - 'بحث_راهنما' => NS_HELP_TALK,
 64+ 'بحث_راهنما' => NS_HELP_TALK,
7165 'رانه‌مای گپ' => NS_HELP_TALK,
7266 'رده' => NS_CATEGORY,
73 - 'بحث_رده' => NS_CATEGORY_TALK,
 67+ 'بحث_رده' => NS_CATEGORY_TALK,
7468 );
7569
7670 $magicWords = array(
Index: trunk/phase3/languages/messages/MessagesSr_ec.php
@@ -35,10 +35,10 @@
3636 NS_USER => 'Корисник',
3737 NS_USER_TALK => 'Разговор_са_корисником',
3838 NS_PROJECT_TALK => 'Разговор_о_$1',
39 - NS_FILE => 'Фајл',
40 - NS_FILE_TALK => 'Разговор_о_фајлу',
41 - NS_MEDIAWIKI => 'МедијаВики',
42 - NS_MEDIAWIKI_TALK => 'Разговор_о_МедијаВикију',
 39+ NS_FILE => 'Датотека',
 40+ NS_FILE_TALK => 'Разговор_о_датотеци',
 41+ NS_MEDIAWIKI => 'Медијавики',
 42+ NS_MEDIAWIKI_TALK => 'Разговор_о_Медијавикију',
4343 NS_TEMPLATE => 'Шаблон',
4444 NS_TEMPLATE_TALK => 'Разговор_о_шаблону',
4545 NS_HELP => 'Помоћ',
@@ -68,10 +68,6 @@
6969
7070 'Слика' => NS_FILE,
7171 'Разговор_о_слици' => NS_FILE_TALK,
72 - "Датотека" => NS_FILE,
73 - "Разговор_о_датотеци" => NS_FILE_TALK,
74 - "Медијавики" => NS_MEDIAWIKI,
75 - "Разговор_о_Медијавикију" => NS_MEDIAWIKI_TALK,
7672 'МедијаВики' => NS_MEDIAWIKI,
7773 'Разговор_о_МедијаВикију' => NS_MEDIAWIKI_TALK,
7874 );
Index: trunk/phase3/languages/messages/MessagesQug.php
@@ -14,25 +14,6 @@
1515
1616 $fallback = 'qu, es';
1717
18 -$namespaceNames = array(
19 - NS_MEDIA => 'Midya',
20 - NS_SPECIAL => 'Sapak',
21 - NS_TALK => 'Rimanakuy',
22 - NS_USER => 'Rurak',
23 - NS_USER_TALK => 'Rurakpa_rimanakuy',
24 - NS_PROJECT_TALK => '$1-pa_rimanakuy',
25 - NS_FILE => 'Rikcha',
26 - NS_FILE_TALK => 'Rikchapa_rimanakuy',
27 - NS_MEDIAWIKI => 'MediaWiki',
28 - NS_MEDIAWIKI_TALK => 'MediaWikipa_rimanakuy',
29 - NS_TEMPLATE => 'Plantilla',
30 - NS_TEMPLATE_TALK => 'Plantillapa_rimanakuy',
31 - NS_HELP => 'Yanapa',
32 - NS_HELP_TALK => 'Yanapapak_rimanakuy',
33 - NS_CATEGORY => 'Samiyachiy',
34 - NS_CATEGORY_TALK => 'Samiyachiy_rimanakuy',
35 -);
36 -
3718 $messages = array(
3819 # User preference toggles
3920 'tog-underline' => 'Tinkikunana uraypi aspishpa rikuchina',
Index: trunk/phase3/languages/messages/MessagesOs.php
@@ -17,16 +17,16 @@
1818 $fallback = 'ru';
1919
2020 $namespaceNames = array(
21 - NS_MEDIA => 'Медиа',
 21+ NS_MEDIA => 'Media',
2222 NS_SPECIAL => 'Сæрмагонд',
2323 NS_TALK => 'Тæрхон',
2424 NS_USER => 'Архайæг',
2525 NS_USER_TALK => 'Архайæджы_ныхас',
26 - NS_PROJECT_TALK => '{{GRAMMAR:genitive|$1}}_тæрхон',
27 - NS_FILE => 'Файл',
28 - NS_FILE_TALK => 'Файлы_тæрхон',
 26+ NS_PROJECT_TALK => 'Дискусси_$1',
 27+ NS_FILE => 'Ныв',
 28+ NS_FILE_TALK => 'Нывы_тæрхон',
2929 NS_MEDIAWIKI => 'MediaWiki',
30 - NS_MEDIAWIKI_TALK => 'MediaWiki-йы_тæрхон',
 30+ NS_MEDIAWIKI_TALK => 'Тæрхон_MediaWiki',
3131 NS_TEMPLATE => 'Шаблон',
3232 NS_TEMPLATE_TALK => 'Шаблоны_тæрхон',
3333 NS_HELP => 'Æххуыс',
@@ -38,19 +38,13 @@
3939 $namespaceAliases = array(
4040 'Дискусси' => NS_TALK,
4141 'Архайæджы_дискусси' => NS_USER_TALK,
42 - 'Дискусси_$1' => NS_PROJECT_TALK,
43 - 'Ныв' => NS_FILE,
44 - 'Нывы_тæрхон' => NS_FILE_TALK,
4542 'Нывы_тыххæй_дискусси' => NS_FILE_TALK,
4643 'Дискусси_MediaWiki' => NS_MEDIAWIKI_TALK,
47 - 'Тæрхон_MediaWiki' => NS_MEDIAWIKI_TALK,
4844 'Шаблоны_тыххæй_дискусси' => NS_TEMPLATE_TALK,
4945 'Æххуысы_тыххæй_дискусси' => NS_HELP_TALK,
5046 'Категорийы_тыххæй_дискусси' => NS_CATEGORY_TALK,
5147 );
5248
53 -// Remove Russian aliases
54 -$namespaceGenderAliases = array();
5549
5650 $magicWords = array(
5751 'redirect' => array( '0', '#РАРВЫСТ', '#перенаправление', '#перенапр', '#REDIRECT' ),
Index: trunk/phase3/languages/messages/MessagesIg.php
@@ -12,38 +12,24 @@
1313 */
1414
1515 $namespaceNames = array(
16 - NS_MEDIA => 'Midia',
 16+ NS_MEDIA => 'Nká',
1717 NS_SPECIAL => 'Ihü_kárírí',
1818 NS_TALK => 'Okwu',
19 - NS_USER => 'Ọbanife',
20 - NS_USER_TALK => 'Okwu_ọbanife',
 19+ NS_USER => 'Ọ\'bànifé',
 20+ NS_USER_TALK => 'Okwu_ọ\'bànifé',
2121 NS_PROJECT_TALK => 'Okwu_$1',
22 - NS_FILE => 'Usòrò',
23 - NS_FILE_TALK => 'Okwu_usòrò',
24 - NS_MEDIAWIKI => 'MidiaWiki',
25 - NS_MEDIAWIKI_TALK => 'Okwu_MidiaWiki',
 22+ NS_FILE => 'Ákwúkwó_orünotu',
 23+ NS_FILE_TALK => 'Okwu_ákwúkwó_orünotu',
 24+ NS_MEDIAWIKI => 'NkáWiki',
 25+ NS_MEDIAWIKI_TALK => 'Okwu_NkáWiki',
2626 NS_TEMPLATE => 'Àtụ',
2727 NS_TEMPLATE_TALK => 'Okwu_àtụ',
28 - NS_HELP => 'Nkwadọ',
29 - NS_HELP_TALK => 'Okwu_nkwadọ',
30 - NS_CATEGORY => 'Òtù',
31 - NS_CATEGORY_TALK => 'Okwu_òtù',
 28+ NS_HELP => 'Nkwádọ',
 29+ NS_HELP_TALK => 'Okwu_nkwádọ',
 30+ NS_CATEGORY => 'Ébéonọr',
 31+ NS_CATEGORY_TALK => 'Okwu_ébéonọr',
3232 );
3333
34 -$namespaceAliases = array(
35 - 'Nká' => NS_MEDIA,
36 - 'Ọ\'bànifé' => NS_USER,
37 - 'Okwu_ọ\'bànifé' => NS_USER_TALK,
38 - 'Ákwúkwó_orünotu' => NS_FILE,
39 - 'Okwu_ákwúkwó_orünotu' => NS_FILE_TALK,
40 - 'NkáWiki' => NS_MEDIAWIKI,
41 - 'Okwu_NkáWiki' => NS_MEDIAWIKI_TALK,
42 - 'Nkwádọ' => NS_HELP,
43 - 'Okwu_nkwádọ' => NS_HELP_TALK,
44 - 'Ébéonọr' => NS_CATEGORY,
45 - 'Okwu_ébéonọr' => NS_CATEGORY_TALK,
46 -);
47 -
4834 $specialPageAliases = array(
4935 'Allpages' => array( 'IhüNílé' ),
5036 'Blankpage' => array( 'HịcháIhü' ),
Index: trunk/phase3/resources/mediawiki.action/mediawiki.action.watch.ajax.js
@@ -4,158 +4,152 @@
55 */
66 ( function ( $, mw, undefined ) {
77
8 - /**
9 - * The name of the page to watch or unwatch.
10 - */
11 - var title = mw.config.get( 'wgRelevantPageName', mw.config.get( 'wgPageName' ) );
 8+/**
 9+ * The name of the page to watch or unwatch.
 10+ */
 11+var title = mw.config.get( 'wgRelevantPageName', mw.config.get( 'wgPageName' ) );
1212
13 - /**
14 - * Update the link text, link href attribute and (if applicable)
15 - * "loading" class.
16 - *
17 - * @param $link {jQuery} Anchor tag of (un)watch link.
18 - * @param action {String} One of 'watch', 'unwatch'.
19 - * @param state {String} [optional] 'idle' or 'loading'. Default is 'idle'.
20 - */
21 - function updateWatchLink( $link, action, state ) {
22 - var accesskeyTip, msgKey, $li;
23 -
24 - // message keys 'watch', 'watching', 'unwatch' or 'unwatching'.
25 - msgKey = state === 'loading' ? action + 'ing' : action;
26 - accesskeyTip = $link.attr( 'title' ).match( mw.util.tooltipAccessKeyRegexp );
 13+/**
 14+ * Update the link text, link href attribute and (if applicable)
 15+ * "loading" class.
 16+ *
 17+ * @param $link {jQuery} Anchor tag of (un)watch link
 18+ * @param action {String} One of 'watch', 'unwatch'.
 19+ * @param state {String} [optional] 'idle' or 'loading'. Default is 'idle'.
 20+ */
 21+function updateWatchLink( $link, action, state ) {
 22+ // message keys 'watch', 'watching', 'unwatch' or 'unwatching'.
 23+ var msgKey = state === 'loading' ? action + 'ing' : action,
 24+ accesskeyTip = $link.attr( 'title' ).match( mw.util.tooltipAccessKeyRegexp ),
2725 $li = $link.closest( 'li' );
2826
29 - $link
30 - .text( mw.msg( msgKey ) )
31 - .attr( 'title', mw.msg( 'tooltip-ca-' + action ) +
32 - ( accesskeyTip ? ' ' + accesskeyTip[0] : '' )
33 - )
34 - .attr( 'href', mw.util.wikiScript() + '?' + $.param({
35 - title: title,
36 - action: action
37 - })
38 - );
 27+ $link
 28+ .text( mw.msg( msgKey ) )
 29+ .attr( 'title', mw.msg( 'tooltip-ca-' + action ) +
 30+ ( accesskeyTip ? ' ' + accesskeyTip[0] : '' )
 31+ )
 32+ .attr( 'href', mw.util.wikiScript() + '?' + $.param({
 33+ title: title,
 34+ action: action
 35+ })
 36+ );
3937
40 - // Special case for vector icon
41 - if ( $li.hasClass( 'icon' ) ) {
42 - if ( state === 'loading' ) {
43 - $link.addClass( 'loading' );
44 - } else {
45 - $link.removeClass( 'loading' );
46 - }
 38+ // Special case for vector icon
 39+ if ( $li.hasClass( 'icon' ) ) {
 40+ if ( state === 'loading' ) {
 41+ $link.addClass( 'loading' );
 42+ } else {
 43+ $link.removeClass( 'loading' );
4744 }
4845 }
 46+}
4947
50 - /**
51 - * @todo This should be moved somewhere more accessible.
52 - * @param url {String}
53 - * @return {String} The extracted action, defaults to 'view'.
54 - */
55 - function mwUriGetAction( url ) {
56 - var action, actionPaths, key, i, m, parts;
 48+/**
 49+ * @todo This should be moved somewhere more accessible.
 50+ * @param url {String}
 51+ * @return {String} The extracted action, defaults to 'view'.
 52+ */
 53+function mwUriGetAction( url ) {
 54+ var actionPaths = mw.config.get( 'wgActionPaths' ),
 55+ key, parts, m, action;
5756
58 - actionPaths = mw.config.get( 'wgActionPaths' );
 57+ // @todo: Does MediaWiki give action path or query param
 58+ // precedence ? If the former, move this to the bottom
 59+ action = mw.util.getParamValue( 'action', url );
 60+ if ( action !== null ) {
 61+ return action;
 62+ }
5963
60 - // @todo: Does MediaWiki give action path or query param
61 - // precedence ? If the former, move this to the bottom
62 - action = mw.util.getParamValue( 'action', url );
63 - if ( action !== null ) {
64 - return action;
65 - }
66 -
67 - for ( key in actionPaths ) {
68 - if ( actionPaths.hasOwnProperty( key ) ) {
69 - parts = actionPaths[key].split( '$1' );
70 - for ( i = 0; i < parts.length; i += 1 ) {
71 - parts[i] = $.escapeRE( parts[i] );
72 - }
73 - m = new RegExp( parts.join( '(.+)' ) ).exec( url );
74 - if ( m && m[1] ) {
75 - return key;
76 - }
77 -
 64+ for ( key in actionPaths ) {
 65+ if ( actionPaths.hasOwnProperty( key ) ) {
 66+ parts = actionPaths[key].split( '$1' );
 67+ for ( i = 0; i < parts.length; i += 1 ) {
 68+ parts[i] = $.escapeRE( parts[i] );
7869 }
 70+ m = new RegExp( parts.join( '(.+)' ) ).exec( url );
 71+ if ( m && m[1] ) {
 72+ return key;
 73+ }
 74+
7975 }
80 -
81 - return 'view';
8276 }
8377
84 - $( document ).ready( function () {
85 - var $links = $( '.mw-watchlink a, a.mw-watchlink, ' +
86 - '#ca-watch a, #ca-unwatch a, #mw-unwatch-link1, ' +
87 - '#mw-unwatch-link2, #mw-watch-link2, #mw-watch-link1' );
 78+ return 'view';
 79+}
8880
89 - // Allowing people to add inline animated links is a little scary
90 - $links = $links.filter( ':not( #bodyContent *, #content * )' );
 81+$( document ).ready( function() {
 82+ var $links = $( '.mw-watchlink a, a.mw-watchlink, ' +
 83+ '#ca-watch a, #ca-unwatch a, #mw-unwatch-link1, ' +
 84+ '#mw-unwatch-link2, #mw-watch-link2, #mw-watch-link1' );
9185
92 - $links.click( function ( e ) {
93 - var action, api, $link;
 86+ // Allowing people to add inline animated links is a little scary
 87+ $links = $links.filter( ':not( #bodyContent *, #content * )' );
9488
 89+ $links.click( function( e ) {
 90+ var $link, api,
9591 action = mwUriGetAction( this.href );
9692
97 - if ( action !== 'watch' && action !== 'unwatch' ) {
98 - // Could not extract target action from link url,
99 - // let native browsing handle it further
100 - return true;
101 - }
102 - e.preventDefault();
103 - e.stopPropagation();
 93+ if ( action !== 'watch' && action !== 'unwatch' ) {
 94+ // Could not extract target action from link url,
 95+ // let native browsing handle it further
 96+ return true;
 97+ }
 98+ e.preventDefault();
 99+ e.stopPropagation();
 100+
 101+ $link = $( this );
104102
105 - $link = $( this );
 103+ updateWatchLink( $link, action, 'loading' );
106104
107 - updateWatchLink( $link, action, 'loading' );
108 -
109 - api = new mw.Api();
110 - api[action](
111 - title,
112 - // Success
113 - function ( watchResponse ) {
114 - var $li, otherAction;
115 -
116 - otherAction = action === 'watch' ? 'unwatch' : 'watch';
 105+ api = new mw.Api();
 106+ api[action](
 107+ title,
 108+ // Success
 109+ function( watchResponse ) {
 110+ var otherAction = action === 'watch' ? 'unwatch' : 'watch',
117111 $li = $link.closest( 'li' );
118112
119 - mw.util.jsMessage( watchResponse.message, 'ajaxwatch' );
 113+ mw.util.jsMessage( watchResponse.message, 'ajaxwatch' );
120114
121 - // Set link to opposite
122 - updateWatchLink( $link, otherAction );
 115+ // Set link to opposite
 116+ updateWatchLink( $link, otherAction );
123117
124 - // Most common ID style
125 - if ( $li.prop( 'id' ) === 'ca-' + otherAction || $li.prop( 'id' ) === 'ca-' + action ) {
126 - $li.prop( 'id', 'ca-' + otherAction );
127 - }
 118+ // Most common ID style
 119+ if ( $li.prop( 'id' ) === 'ca-' + otherAction || $li.prop( 'id' ) === 'ca-' + action ) {
 120+ $li.prop( 'id', 'ca-' + otherAction );
 121+ }
 122+
 123+ // Bug 12395 - update the watch checkbox on edit pages when the
 124+ // page is watched or unwatched via the tab.
 125+ if ( watchResponse.watched !== undefined ) {
 126+ $( '#wpWatchthis' ).prop( 'checked', true );
 127+ } else {
 128+ $( '#wpWatchthis' ).removeProp( 'checked' );
 129+ }
 130+ },
 131+ // Error
 132+ function(){
128133
129 - // Bug 12395 - update the watch checkbox on edit pages when the
130 - // page is watched or unwatched via the tab.
131 - if ( watchResponse.watched !== undefined ) {
132 - $( '#wpWatchthis' ).prop( 'checked', true );
133 - } else {
134 - $( '#wpWatchthis' ).removeProp( 'checked' );
135 - }
136 - },
137 - // Error
138 - function () {
139 - var cleanTitle, html, link;
 134+ // Reset link to non-loading mode
 135+ updateWatchLink( $link, action );
 136+
 137+ // Format error message
 138+ var cleanTitle = title.replace( /_/g, ' ' );
 139+ var link = mw.html.element(
 140+ 'a', {
 141+ 'href': mw.util.wikiGetlink( title ),
 142+ 'title': cleanTitle
 143+ }, cleanTitle
 144+ );
 145+ var html = mw.msg( 'watcherrortext', link );
 146+
 147+ // Report to user about the error
 148+ mw.util.jsMessage( html, 'ajaxwatch' );
140149
141 - // Reset link to non-loading mode
142 - updateWatchLink( $link, action );
143 -
144 - // Format error message
145 - cleanTitle = title.replace( /_/g, ' ' );
146 - link = mw.html.element(
147 - 'a', {
148 - href: mw.util.wikiGetlink( title ),
149 - title: cleanTitle
150 - }, cleanTitle
151 - );
152 - html = mw.msg( 'watcherrortext', link );
153 -
154 - // Report to user about the error
155 - mw.util.jsMessage( html, 'ajaxwatch' );
156 -
157 - }
158 - );
159 - });
 150+ }
 151+ );
160152 });
161153
162 -}( jQuery, mediaWiki ) );
 154+});
 155+
 156+})( jQuery, mediaWiki );
Index: trunk/phase3/resources/mediawiki.api/mediawiki.api.watch.js
@@ -4,51 +4,47 @@
55 */
66 ( function( $, mw ) {
77
8 - /**
9 - * @context {mw.Api}
10 - */
11 - function doWatchInternal( page, success, err, addParams ) {
12 - var params = {
13 - action: 'watch',
14 - title: String( page ),
15 - token: mw.user.tokens.get( 'watchToken' ),
16 - uselang: mw.config.get( 'wgUserLanguage' )
17 - };
18 - function ok( data ) {
19 - success( data.watch );
20 - }
21 - if ( addParams ) {
22 - $.extend( params, addParams );
23 - }
24 - return this.post( params, { ok: ok, err: err } );
25 - }
26 -
278 $.extend( mw.Api.prototype, {
289 /**
2910 * Convinience method for 'action=watch'.
3011 *
3112 * @param page {String|mw.Title} Full page name or instance of mw.Title
32 - * @param success {Function} Callback to which the watch object will be passed.
33 - * Watch object contains properties 'title' (full pagename), 'watched' (boolean) and
 13+ * @param success {Function} callback to which the watch object will be passed
 14+ * watch object contains 'title' (full page name), 'watched' (boolean) and
3415 * 'message' (parsed HTML of the 'addedwatchtext' message).
35 - * @param err {Function} Error callback (optional)
 16+ * @param _unwatch {Boolean} Internally used to re-use this logic for unwatch(),
 17+ * do not use outside this module.
 18+ * @param err {Function} callback if error (optional)
3619 * @return {jqXHR}
3720 */
38 - watch: function ( page, success, err ) {
39 - return doWatchInternal.call( this, page, success, err );
 21+ watch: function( page, success, err, _unwatch ) {
 22+ var params, ok;
 23+ params = {
 24+ action: 'watch',
 25+ title: String( page ),
 26+ token: mw.user.tokens.get( 'watchToken' ),
 27+ uselang: mw.config.get( 'wgUserLanguage' )
 28+ };
 29+ if ( _unwatch ) {
 30+ params.unwatch = 1;
 31+ }
 32+ ok = function( data ) {
 33+ success( data.watch );
 34+ };
 35+ return this.post( params, { ok: ok, err: err } );
4036 },
4137 /**
4238 * Convinience method for 'action=watch&unwatch=1'.
4339 *
4440 * @param page {String|mw.Title} Full page name or instance of mw.Title
45 - * @param success {Function} Callback to which the watch object will be passed.
46 - * Watch object contains properties 'title' (full pagename), 'watched' (boolean) and
 41+ * @param success {Function} callback to which the watch object will be passed
 42+ * watch object contains 'title' (full page name), 'unwatched' (boolean) and
4743 * 'message' (parsed HTML of the 'removedwatchtext' message).
48 - * @param err {Function} Error callback (optional)
 44+ * @param err {Function} callback if error (optional)
4945 * @return {jqXHR}
5046 */
51 - unwatch: function ( page, success, err ) {
52 - return doWatchInternal.call( this, page, success, err, { unwatch: 1 } );
 47+ unwatch: function( page, success, err ) {
 48+ return this.watch( page, success, err, true );
5349 }
5450
5551 } );

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r107309Localisation updates for namespace names for core from http://translatewiki.net.siebrand23:58, 25 December 2011
r113601Bug 35162 - Change in Database api break extension MSSQLBackCompat...reedy23:40, 11 March 2012
r113704[FileBackend]...aaron01:46, 13 March 2012
r113742[FileBackend] r113704: updated FileBackend constructor documentationaaron18:30, 13 March 2012
r113792(bug 32827) "[Regression] Block log for IP ranges not shown on Special:Block"aaron01:42, 14 March 2012
r113838Follow-up to r15791: Rename "user" and "text" when upgrading on PostgreSQL...saper20:20, 14 March 2012
r113859[FileRepo]...aaron21:30, 14 March 2012
r113893[mediawiki.action.watch] Move re-used logic into local function...krinkle05:54, 15 March 2012
r113894[mediawiki.action.watch] clean up...krinkle06:04, 15 March 2012
r113952[FileRepo] Tweaked transformErrorOutput() to allow more useful error output (...aaron20:37, 15 March 2012
r114047fix for bug33214 - catch all exceptions in api execute and provides necessary...bsitu02:23, 17 March 2012
r114252Refactoring dumpTextPass's error handlingqchris11:11, 20 March 2012
r114256Follow up to r114252: Removing assertsqchris11:53, 20 March 2012
r114257Follow-up to r114256: Removing final assertqchris12:00, 20 March 2012

Status & tagging log