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
'FSFileBackendFileList' => 'includes/filerepo/backend/FSFileBackend.php',
'SwiftFileBackend' => 'includes/filerepo/backend/SwiftFileBackend.php',
'SwiftFileBackendFileList' => 'includes/filerepo/backend/SwiftFileBackend.php',
- 'FileJournal' => 'includes/filerepo/backend/filejournal/FileJournal.php',
- 'DBFileJournal' => 'includes/filerepo/backend/filejournal/DBFileJournal.php',
- 'NullFileJournal' => 'includes/filerepo/backend/filejournal/FileJournal.php',
'LockManagerGroup' => 'includes/filerepo/backend/lockmanager/LockManagerGroup.php',
'LockManager' => 'includes/filerepo/backend/lockmanager/LockManager.php',
'ScopedLock' => 'includes/filerepo/backend/lockmanager/LockManager.php',
}
public function execute() {
+ $params = $this->extractRequestParams();
global $wgFeed, $wgFeedClasses, $wgSitename, $wgLanguageCode;
- try {
- $params = $this->extractRequestParams();
-
- if( !$wgFeed ) {
- $this->dieUsage( 'Syndication feeds are not available', 'feed-unavailable' );
- }
-
- if( !isset( $wgFeedClasses[ $params['feedformat'] ] ) ) {
- $this->dieUsage( 'Invalid subscription feed type', 'feed-invalid' );
- }
+ if( !$wgFeed ) {
+ $this->dieUsage( 'Syndication feeds are not available', 'feed-unavailable' );
+ }
- global $wgMiserMode;
- if ( $params['showsizediff'] && $wgMiserMode ) {
- $this->dieUsage( 'Size difference is disabled in Miser Mode', 'sizediffdisabled' );
- }
-
- $msg = wfMsgForContent( 'Contributions' );
- $feedTitle = $wgSitename . ' - ' . $msg . ' [' . $wgLanguageCode . ']';
- $feedUrl = SpecialPage::getTitleFor( 'Contributions', $params['user'] )->getFullURL();
-
- $target = $params['user'] == 'newbies'
- ? 'newbies'
- : Title::makeTitleSafe( NS_USER, $params['user'] )->getText();
-
- $feed = new $wgFeedClasses[$params['feedformat']] (
- $feedTitle,
- htmlspecialchars( $msg ),
- $feedUrl
- );
-
- $pager = new ContribsPager( $this->getContext(), array(
- 'target' => $target,
- 'namespace' => $params['namespace'],
- 'year' => $params['year'],
- 'month' => $params['month'],
- 'tagFilter' => $params['tagfilter'],
- 'deletedOnly' => $params['deletedonly'],
- 'topOnly' => $params['toponly'],
- 'showSizeDiff' => $params['showsizediff'],
- ) );
-
- $feedItems = array();
- if( $pager->getNumRows() > 0 ) {
- foreach ( $pager->mResult as $row ) {
- $feedItems[] = $this->feedItem( $row );
- }
- }
-
- ApiFormatFeedWrapper::setResult( $this->getResult(), $feed, $feedItems );
-
- } catch ( Exception $e ) {
- // Error results should not be cached
- $this->getMain()->setCacheMaxAge( 0 );
-
- $feedTitle = $wgSitename . ' - Error - ' . wfMsgForContent( 'contributions' ) . ' [' . $wgLanguageCode . ']';
- $feedUrl = SpecialPage::getTitleFor( 'Contributions', $params['user'] )->getFullURL();
-
- $feedFormat = isset( $params['feedformat'] ) ? $params['feedformat'] : 'rss';
- $feed = new $wgFeedClasses[$feedFormat] ( $feedTitle, htmlspecialchars( wfMsgForContent( 'contributions' ) ), $feedUrl );
-
- if ( $e instanceof UsageException ) {
- $errorCode = $e->getCodeString();
- } else {
- // Something is seriously wrong
- $errorCode = 'internal_api_error';
- }
+ if( !isset( $wgFeedClasses[ $params['feedformat'] ] ) ) {
+ $this->dieUsage( 'Invalid subscription feed type', 'feed-invalid' );
+ }
+
+ global $wgMiserMode;
+ if ( $params['showsizediff'] && $wgMiserMode ) {
+ $this->dieUsage( 'Size difference is disabled in Miser Mode', 'sizediffdisabled' );
+ }
+
+ $msg = wfMsgForContent( 'Contributions' );
+ $feedTitle = $wgSitename . ' - ' . $msg . ' [' . $wgLanguageCode . ']';
+ $feedUrl = SpecialPage::getTitleFor( 'Contributions', $params['user'] )->getFullURL();
+
+ $target = $params['user'] == 'newbies'
+ ? 'newbies'
+ : Title::makeTitleSafe( NS_USER, $params['user'] )->getText();
+
+ $feed = new $wgFeedClasses[$params['feedformat']] (
+ $feedTitle,
+ htmlspecialchars( $msg ),
+ $feedUrl
+ );
- $errorText = $e->getMessage();
- $feedItems[] = new FeedItem( "Error ($errorCode)", $errorText, '', '', '' );
- ApiFormatFeedWrapper::setResult( $this->getResult(), $feed, $feedItems );
+ $pager = new ContribsPager( $this->getContext(), array(
+ 'target' => $target,
+ 'namespace' => $params['namespace'],
+ 'year' => $params['year'],
+ 'month' => $params['month'],
+ 'tagFilter' => $params['tagfilter'],
+ 'deletedOnly' => $params['deletedonly'],
+ 'topOnly' => $params['toponly'],
+ 'showSizeDiff' => $params['showsizediff'],
+ ) );
+
+ $feedItems = array();
+ if( $pager->getNumRows() > 0 ) {
+ foreach ( $pager->mResult as $row ) {
+ $feedItems[] = $this->feedItem( $row );
+ }
}
+
+ ApiFormatFeedWrapper::setResult( $this->getResult(), $feed, $feedItems );
}
protected function feedItem( $row ) {
$dbType = strtolower( $dbType );
$class = 'Database' . ucfirst( $dbType );
- if( in_array( $dbType, $canonicalDBTypes ) || ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) ) {
+ if( in_array( $dbType, $canonicalDBTypes ) ) {
return new $class(
isset( $p['host'] ) ? $p['host'] : false,
isset( $p['user'] ) ? $p['user'] : false,
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;
}
# Replace reserved words with better ones
switch( $name ) {
case 'user':
- return $this->realTableName( 'mwuser', $format );
+ return 'mwuser';
case 'text':
- return $this->realTableName( 'pagecontent', $format );
+ return 'pagecontent';
default:
- return $this->realTableName( $name, $format );
+ return parent::tableName( $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
if ( !$schema ) {
$schema = $this->getCoreSchema();
}
- $table = $this->realTableName( $table, 'raw' );
+ $table = $this->tableName( $table, 'raw' );
$etable = $this->addQuotes( $table );
$eschema = $this->addQuotes( $schema );
$SQL = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
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.
protected $readOnly; // string; read-only explanation message
/** @var LockManager */
protected $lockManager;
- /** @var FileJournal */
- protected $fileJournal;
/**
* Create a new backend instance from configuration.
* This should consist of alphanumberic, '-', and '_' characters.
* This name should not be changed after use.
* 'wikiId' : Prefix to container names that is unique to this wiki.
- * It should only consist of alphanumberic, '-', and '_' characters.
+ * This should consist of alphanumberic, '-', and '_' characters.
* 'lockManager' : Registered name of a file lock manager to use.
- * 'fileJournal' : File journal configuration; see FileJournal::factory().
- * Journals simply log changes to files stored in the backend.
* 'readOnly' : Write operations are disallowed if this is a non-empty string.
* It should be an explanation for the backend being read-only.
*
$this->lockManager = ( $config['lockManager'] instanceof LockManager )
? $config['lockManager']
: LockManagerGroup::singleton()->get( $config['lockManager'] );
- $this->fileJournal = isset( $config['fileJournal'] )
- ? FileJournal::factory( $config['fileJournal'], $this->name )
- : FileJournal::factory( array( 'class' => 'NullFileJournal' ), $this->name );
$this->readOnly = isset( $config['readOnly'] )
? (string)$config['readOnly']
: '';
* 'allowStale' : Don't require the latest available data.
* This can increase performance for non-critical writes.
* This has no effect unless the 'force' flag is set.
- * 'nonJournaled' : Don't log this operation batch in the file journal.
- * This limits the ability of recovery scripts.
*
* Remarks on locking:
* File system paths given to operations should refer to files that are
}
// Actually attempt the operation batch...
- $subStatus = FileOp::attemptBatch( $performOps, $opts, $this->fileJournal );
+ $subStatus = FileOp::attemptBatch( $performOps, $opts );
$success = array();
$failCount = 0;
$this->clearCache();
// Actually attempt the operation batch...
- $subStatus = FileOp::attemptBatch( $performOps, $opts, $this->fileJournal );
+ $subStatus = FileOp::attemptBatch( $performOps, $opts );
// Merge errors into status fields
$status->merge( $subStatus );
protected $state = self::STATE_NEW; // integer
protected $failed = false; // boolean
protected $useLatest = true; // boolean
- protected $batchId; // string
protected $sourceSha1; // string
protected $destSameAsSource; // boolean
$this->params = $params;
}
- /**
- * Set the batch UUID this operation belongs to
- *
- * @param $batchId string
- * @return void
- */
- final protected function setBatchId( $batchId ) {
- $this->batchId = $batchId;
- }
-
/**
* Whether to allow stale data for file reads and stat checks
*
}
/**
- * Attempt to perform a series of file operations.
+ * Attempt a series of file operations.
* Callers are responsible for handling file locking.
*
* $opts is an array of options, including:
- * 'force' : Errors that would normally cause a rollback do not.
- * The remaining operations are still attempted if any fail.
- * 'allowStale' : Don't require the latest available data.
- * This can increase performance for non-critical writes.
- * This has no effect unless the 'force' flag is set.
- * 'nonJournaled' : Don't log this operation batch in the file journal.
- *
+ * 'force' : Errors that would normally cause a rollback do not.
+ * The remaining operations are still attempted if any fail.
+ * 'allowStale' : Don't require the latest available data.
+ * This can increase performance for non-critical writes.
+ * This has no effect unless the 'force' flag is set.
+ *
* The resulting Status will be "OK" unless:
* a) unexpected operation errors occurred (network partitions, disk full...)
* b) significant operation errors occured and 'force' was not set
*
* @param $performOps Array List of FileOp operations
* @param $opts Array Batch operation options
- * @param $journal FileJournal Journal to log operations to
* @return Status
*/
- final public static function attemptBatch(
- array $performOps, array $opts, FileJournal $journal
- ) {
+ final public static function attemptBatch( array $performOps, array $opts ) {
$status = Status::newGood();
+ $allowStale = !empty( $opts['allowStale'] );
+ $ignoreErrors = !empty( $opts['force'] );
+
$n = count( $performOps );
if ( $n > self::MAX_BATCH_SIZE ) {
$status->fatal( 'backend-fail-batchsize', $n, self::MAX_BATCH_SIZE );
return $status;
}
- $batchId = $journal->getTimestampedUUID();
- $allowStale = !empty( $opts['allowStale'] );
- $ignoreErrors = !empty( $opts['force'] );
- $journaled = empty( $opts['nonJournaled'] );
-
- $entries = array(); // file journal entries
$predicates = FileOp::newPredicates(); // account for previous op in prechecks
// Do pre-checks for each operation; abort on failure...
foreach ( $performOps as $index => $fileOp ) {
- $fileOp->setBatchId( $batchId );
$fileOp->allowStaleReads( $allowStale );
- $oldPredicates = $predicates;
- $subStatus = $fileOp->precheck( $predicates ); // updates $predicates
+ $subStatus = $fileOp->precheck( $predicates );
$status->merge( $subStatus );
- if ( $subStatus->isOK() ) {
- if ( $journaled ) { // journal log entry
- $entries = array_merge( $entries,
- self::getJournalEntries( $fileOp, $oldPredicates, $predicates ) );
- }
- } else { // operation failed?
+ if ( !$subStatus->isOK() ) { // operation failed?
$status->success[$index] = false;
++$status->failCount;
if ( !$ignoreErrors ) {
}
}
- // Log the operations in file journal...
- if ( count( $entries ) ) {
- $subStatus = $journal->logChangeBatch( $entries, $batchId );
- if ( !$subStatus->isOK() ) {
- return $subStatus; // abort
- }
- }
-
- if ( $ignoreErrors ) { // treat precheck() fatals as mere warnings
+ if ( $ignoreErrors ) {
+ # Treat all precheck() fatals as merely warnings
$status->setResult( true, $status->value );
}
return $status;
}
- /**
- * Get the file journal entries for a single file operation
- *
- * @param $fileOp FileOp
- * @param $oPredicates Array Pre-op information about files
- * @param $nPredicates Array Post-op information about files
- * @return Array
- */
- final protected static function getJournalEntries(
- FileOp $fileOp, array $oPredicates, array $nPredicates
- ) {
- $nullEntries = array();
- $updateEntries = array();
- $deleteEntries = array();
- $pathsUsed = array_merge( $fileOp->storagePathsRead(), $fileOp->storagePathsChanged() );
- foreach ( $pathsUsed as $path ) {
- $nullEntries[] = array( // assertion for recovery
- 'op' => 'null',
- 'path' => $path,
- 'newSha1' => $fileOp->fileSha1( $path, $oPredicates )
- );
- }
- foreach ( $fileOp->storagePathsChanged() as $path ) {
- if ( $nPredicates['sha1'][$path] === false ) { // deleted
- $deleteEntries[] = array(
- 'op' => 'delete',
- 'path' => $path,
- 'newSha1' => ''
- );
- } else { // created/updated
- $updateEntries[] = array(
- 'op' => $fileOp->fileExists( $path, $oPredicates ) ? 'update' : 'create',
- 'path' => $path,
- 'newSha1' => $nPredicates['sha1'][$path]
- );
- }
- }
- return array_merge( $nullEntries, $updateEntries, $deleteEntries );
- }
-
/**
* Get the value of the parameter with the given name
*
$params = $this->params;
$params['failedAction'] = $action;
try {
- wfDebugLog( 'FileOperation', get_class( $this ) .
- " failed (batch #{$this->batchId}): " . FormatJson::encode( $params ) );
+ wfDebugLog( 'FileOperation',
+ get_class( $this ) . ' failed: ' . FormatJson::encode( $params ) );
} catch ( Exception $e ) {
// bad config? debug log error?
}
+++ /dev/null
-<?php
-/**
- * @file
- * @ingroup FileJournal
- * @author Aaron Schulz
- */
-
-/**
- * Version of FileJournal that logs to a DB table
- * @since 1.20
- */
-class DBFileJournal extends FileJournal {
- protected $wiki = false; // string; wiki DB name
-
- /**
- * Construct a new instance from configuration.
- * $config includes:
- * 'wiki' : wiki name to use for LoadBalancer
- *
- * @param $config Array
- */
- protected function __construct( array $config ) {
- parent::__construct( $config );
-
- $this->wiki = $config['wiki'];
- }
-
- /**
- * @see FileJournal::logChangeBatch()
- * @return Status
- */
- protected function doLogChangeBatch( array $entries, $batchId ) {
- $status = Status::newGood();
-
- $dbw = $this->getMasterDB();
- if ( !$dbw ) {
- $status->fatal( 'filejournal-fail-dbconnect', $this->backend );
- return $status;
- }
- $now = wfTimestamp( TS_UNIX );
-
- $data = array();
- foreach ( $entries as $entry ) {
- $data[] = array(
- 'fj_batch_uuid' => $batchId,
- 'fj_backend' => $this->backend,
- 'fj_op' => $entry['op'],
- 'fj_path' => $entry['path'],
- 'fj_path_sha1' => wfBaseConvert( sha1( $entry['path'] ), 16, 36, 31 ),
- 'fj_new_sha1' => $entry['newSha1'],
- 'fj_timestamp' => $dbw->timestamp( $now )
- );
- }
-
- try {
- $dbw->begin();
- $dbw->insert( 'filejournal', $data, __METHOD__ );
- $dbw->commit();
- } catch ( DBError $e ) {
- $status->fatal( 'filejournal-fail-dbquery', $this->backend );
- return $status;
- }
-
- return $status;
- }
-
- /**
- * @see FileJournal::purgeOldLogs()
- * @return Status
- */
- protected function doPurgeOldLogs() {
- $status = Status::newGood();
- if ( $this->ttlDays <= 0 ) {
- return $status; // nothing to do
- }
-
- $dbw = $this->getMasterDB();
- if ( !$dbw ) {
- $status->fatal( 'filejournal-fail-dbconnect', $this->backend );
- return $status;
- }
- $dbCutoff = $dbw->timestamp( time() - 86400 * $this->ttlDays );
-
- try {
- $dbw->begin();
- $dbw->delete( 'filejournal',
- array( 'fj_timestamp < ' . $dbw->addQuotes( $dbCutoff ) ),
- __METHOD__
- );
- $dbw->commit();
- } catch ( DBError $e ) {
- $status->fatal( 'filejournal-fail-dbquery', $this->backend );
- return $status;
- }
-
- return $status;
- }
-
- /**
- * Get a master connection to the logging DB
- *
- * @return DatabaseBase|null
- */
- protected function getMasterDB() {
- try {
- $lb = wfGetLBFactory()->newMainLB();
- return $lb->getConnection( DB_MASTER, array(), $this->wiki );
- } catch ( DBConnectionError $e ) {
- return null;
- }
- }
-}
+++ /dev/null
-<?php
-/**
- * @defgroup FileJournal File journal
- * @ingroup FileBackend
- */
-
-/**
- * @file
- * @ingroup FileJournal
- * @author Aaron Schulz
- */
-
-/**
- * @brief Class for handling file operation journaling.
- *
- * Subclasses should avoid throwing exceptions at all costs.
- *
- * @ingroup FileJournal
- * @since 1.20
- */
-abstract class FileJournal {
- protected $backend; // string
- protected $ttlDays; // integer
-
- /**
- * Construct a new instance from configuration.
- * $config includes:
- * 'ttlDays' : days to keep log entries around (false means "forever")
- *
- * @param $config Array
- */
- protected function __construct( array $config ) {
- $this->ttlDays = isset( $config['ttlDays'] ) ? $config['ttlDays'] : false;
- }
-
- /**
- * Create an appropriate FileJournal object from config
- *
- * @param $config Array
- * @param $backend string A registered file backend name
- * @return FileJournal
- */
- final public static function factory( array $config, $backend ) {
- $class = $config['class'];
- $jrn = new $class( $config );
- if ( !$jrn instanceof self ) {
- throw new MWException( "Class given is not an instance of FileJournal." );
- }
- $jrn->backend = $backend;
- return $jrn;
- }
-
- /**
- * Get a statistically unique ID string
- *
- * @return string <9 char TS_MW timestamp in base 36><22 random base 36 chars>
- */
- final public function getTimestampedUUID() {
- $s = '';
- for ( $i = 0; $i < 5; $i++ ) {
- $s .= mt_rand( 0, 2147483647 );
- }
- $s = wfBaseConvert( sha1( $s ), 16, 36, 31 );
- return substr( wfBaseConvert( wfTimestamp( TS_MW ), 10, 36, 9 ) . $s, 0, 31 );
- }
-
- /**
- * Log changes made by a batch file operation.
- * $entries is an array of log entries, each of which contains:
- * op : Basic operation name (create, store, copy, delete)
- * path : The storage path of the file
- * newSha1 : The final base 36 SHA-1 of the file
- * Note that 'false' should be used as the SHA-1 for non-existing files.
- *
- * @param $entries Array List of file operations (each an array of parameters)
- * @param $batchId string UUID string that identifies the operation batch
- * @return Status
- */
- final public function logChangeBatch( array $entries, $batchId ) {
- if ( !count( $entries ) ) {
- return Status::newGood();
- }
- return $this->doLogChangeBatch( $entries, $batchId );
- }
-
- /**
- * @see FileJournal::logChangeBatch()
- *
- * @param $entries Array List of file operations (each an array of parameters)
- * @param $batchId string UUID string that identifies the operation batch
- * @return Status
- */
- abstract protected function doLogChangeBatch( array $entries, $batchId );
-
- /**
- * Purge any old log entries
- *
- * @return Status
- */
- final public function purgeOldLogs() {
- return $this->doPurgeOldLogs();
- }
-
- /**
- * @see FileJournal::purgeOldLogs()
- * @return Status
- */
- abstract protected function doPurgeOldLogs();
-}
-
-/**
- * Simple version of FileJournal that does nothing
- * @since 1.20
- */
-class NullFileJournal extends FileJournal {
- /**
- * @see FileJournal::logChangeBatch()
- * @return Status
- */
- protected function doLogChangeBatch( array $entries, $batchId ) {
- return Status::newGood();
- }
-
- /**
- * @see FileJournal::purgeOldLogs()
- * @return Status
- */
- protected function doPurgeOldLogs() {
- return Status::newGood();
- }
-}
* @param $thumbUrl string Thumbnail URL
* @param $params Array
* @param $flags integer
- * @param $status Status Optional status object to use for errors
* @return MediaTransformOutput
*/
- protected function transformErrorOutput(
- $thumbPath, $thumbUrl, $params, $flags, Status $status = null
- ) {
+ protected function transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ) {
global $wgIgnoreImageErrors;
if ( $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) {
return $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
} else {
- $badStatus = Status::newFatal( 'thumbnail-dest-create' );
- if ( $status ) { // additional, more detailed errors
- $badStatus->merge( $status );
- }
- $err = array();
- foreach ( $badStatus->getErrorsArray() as $item ) {
- $err[] = wfMsg( $item[0], array_slice( $item, 1 ) );
- }
return new MediaTransformError( 'thumbnail_error',
- $params['width'], 0, implode( "\n", $err ) ); // MTO does "\n" => "<br/>"
+ $params['width'], 0, wfMsg( 'thumbnail-dest-create' ) );
}
}
if ( $status->isOK() ) {
$thumb->setStoragePath( $thumbPath );
} else {
- $thumb = $this->transformErrorOutput(
- $thumbPath, $thumbUrl, $params, $flags, $status );
+ $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags );
}
}
*/
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 );
* 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();
* @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
* @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 );
}
$status = $batch->execute();
- if ( $status->isOK() ) {
+ if ( $status->ok ) {
// Update site_stats
$site_stats = $dbw->tableName( 'site_stats' );
$dbw->query( "UPDATE $site_stats SET ss_images=ss_images-1", __METHOD__ );
* @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 );
$this->unlock(); // done
- if ( $status->isOK() ) {
+ if ( $status->ok ) {
$this->purgeDescription();
$this->purgeHistory();
}
* @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 ) {
$status = $batch->execute();
- if ( $status->isGood() ) {
- $cleanupStatus = $batch->cleanup();
- $cleanupStatus->successCount = 0;
- $cleanupStatus->failCount = 0;
- $status->merge( $cleanupStatus );
+ if ( !$status->isGood() ) {
+ return $status;
}
- $this->unlock(); // done
+ $cleanupStatus = $batch->cleanup();
+ $cleanupStatus->successCount = 0;
+ $cleanupStatus->failCount = 0;
+ $status->merge( $cleanupStatus );
return $status;
}
$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
# ------------------------------------------------------------------------------
$this->status->merge( $status );
}
- if ( !$this->status->isOK() ) {
+ if ( !$this->status->ok ) {
// 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
}
$this->db->selectDB( $this->getVar( 'wgDBname' ) );
- if( $this->db->tableExists( 'archive', __METHOD__ ) ) {
+ if( $this->db->tableExists( 'user', __METHOD__ ) ) {
$status->warning( 'config-install-tables-exist' );
$this->enableLB();
return $status;
*/
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' ),
protected function renameTable( $old, $new ) {
if ( $this->db->tableExists( $old ) ) {
$this->output( "Renaming table $old to $new\n" );
- $old = $this->db->realTableName( $old, "quoted" );
- $new = $this->db->realTableName( $new, "quoted" );
+ $old = $this->db->addQuotes( $old );
$this->db->query( "ALTER TABLE $old RENAME TO $new" );
}
}
$this->getLanguage()->pipeList( $links )
);
- $userTitle = self::getTargetUserTitle( $this->target );
- if( $userTitle ){
+ if( $this->target instanceof User ){
# Get relevant extracts from the block and suppression logs, if possible
+ $userpage = $this->target->getUserPage();
$out = '';
LogEventsList::showLogExtract(
$out,
'block',
- $userTitle,
+ $userpage,
'',
array(
'lim' => 10,
- 'msgKey' => array( 'blocklog-showlog', $userTitle->getText() ),
+ 'msgKey' => array( 'blocklog-showlog', $userpage->getText() ),
'showIfEmpty' => false
)
);
LogEventsList::showLogExtract(
$out,
'suppress',
- $userTitle,
+ $userpage,
'',
array(
'lim' => 10,
'conds' => array( 'log_action' => array( 'block', 'reblock', 'unblock' ) ),
- 'msgKey' => array( 'blocklog-showsuppresslog', $userTitle->getText() ),
+ 'msgKey' => array( 'blocklog-showsuppresslog', $userpage->getText() ),
'showIfEmpty' => false
)
);
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?
'backend-fail-contenttype' => 'Could not determine the content type of the file to store at "$1".',
'backend-fail-batchsize' => 'Storage backend given a batch of $1 file {{PLURAL:$1|operation|operations}}; the limit is $2 {{PLURAL:$2|operation|operations}}.',
-'filejournal-fail-dbconnect' => 'Could not connect to the journal database for storage backend "$1".',
-'filejournal-fail-dbquery' => 'Could not update the journal database for storage backend "$1".',
-
# Lock manager
'lockmanager-notlocked' => 'Could not unlock "$1"; it is not locked.',
'lockmanager-fail-closelock' => 'Could not close lock file for "$1".',
NS_MEDIA => 'Mèdia',
NS_SPECIAL => 'Spèciâl',
NS_TALK => 'Discussion',
- NS_USER => 'Usanciér',
- NS_USER_TALK => 'Discussion_usanciér',
+ NS_USER => 'Utilisator',
+ NS_USER_TALK => 'Discussion_utilisator',
NS_PROJECT_TALK => 'Discussion_$1',
NS_FILE => 'Fichiér',
NS_FILE_TALK => 'Discussion_fichiér',
);
$namespaceAliases = array(
- 'Discutar' => NS_TALK,
- 'Utilisator' => NS_USER,
+ 'Discutar' => NS_TALK,
'Discussion_Utilisator' => NS_USER_TALK,
- 'Émâge' => NS_FILE,
- 'Discussion_Émâge' => NS_FILE_TALK,
- 'Discussion_Modèlo' => NS_TEMPLATE_TALK,
- 'Discussion_Éde' => NS_HELP_TALK,
- 'Discussion_Catègorie' => NS_CATEGORY_TALK
+ 'Émâge' => NS_FILE,
+ 'Discussion_Émâge' => NS_FILE_TALK,
+ 'Discussion_Modèlo' => NS_TEMPLATE_TALK,
+ 'Discussion_Éde' => NS_HELP_TALK,
+ 'Discussion_Catègorie' => NS_CATEGORY_TALK
);
$specialPageAliases = array(
*/
$namespaceNames = array(
- NS_MEDIA => 'Midia',
+ NS_MEDIA => 'Nká',
NS_SPECIAL => 'Ihü_kárírí',
NS_TALK => 'Okwu',
- NS_USER => 'Ọbanife',
- NS_USER_TALK => 'Okwu_ọbanife',
+ NS_USER => 'Ọ\'bànifé',
+ NS_USER_TALK => 'Okwu_ọ\'bànifé',
NS_PROJECT_TALK => 'Okwu_$1',
- NS_FILE => 'Usòrò',
- NS_FILE_TALK => 'Okwu_usòrò',
- NS_MEDIAWIKI => 'MidiaWiki',
- NS_MEDIAWIKI_TALK => 'Okwu_MidiaWiki',
+ NS_FILE => 'Ákwúkwó_orünotu',
+ NS_FILE_TALK => 'Okwu_ákwúkwó_orünotu',
+ NS_MEDIAWIKI => 'NkáWiki',
+ NS_MEDIAWIKI_TALK => 'Okwu_NkáWiki',
NS_TEMPLATE => 'Àtụ',
NS_TEMPLATE_TALK => 'Okwu_àtụ',
- NS_HELP => 'Nkwadọ',
- NS_HELP_TALK => 'Okwu_nkwadọ',
- NS_CATEGORY => 'Òtù',
- NS_CATEGORY_TALK => 'Okwu_òtù',
-);
-
-$namespaceAliases = array(
- 'Nká' => NS_MEDIA,
- 'Ọ\'bànifé' => NS_USER,
- 'Okwu_ọ\'bànifé' => NS_USER_TALK,
- 'Ákwúkwó_orünotu' => NS_FILE,
- 'Okwu_ákwúkwó_orünotu' => NS_FILE_TALK,
- 'NkáWiki' => NS_MEDIAWIKI,
- 'Okwu_NkáWiki' => NS_MEDIAWIKI_TALK,
- 'Nkwádọ' => NS_HELP,
- 'Okwu_nkwádọ' => NS_HELP_TALK,
- 'Ébéonọr' => NS_CATEGORY,
- 'Okwu_ébéonọr' => NS_CATEGORY_TALK,
+ NS_HELP => 'Nkwádọ',
+ NS_HELP_TALK => 'Okwu_nkwádọ',
+ NS_CATEGORY => 'Ébéonọr',
+ NS_CATEGORY_TALK => 'Okwu_ébéonọr',
);
$specialPageAliases = array(
*/
$namespaceNames = array(
- NS_MEDIA => 'Ð\9cедиÑ\83м',
+ NS_MEDIA => 'Ð\9cедиÑ\98а',
NS_SPECIAL => 'Специјална',
NS_TALK => 'Разговор',
NS_USER => 'Корисник',
);
$namespaceAliases = array(
- 'Медија' => NS_MEDIA,
- 'Специјални' => NS_SPECIAL,
- 'Слика' => NS_FILE,
+ 'Специјални' => NS_SPECIAL,
+ 'Слика' => NS_FILE,
'Разговор_за_слика' => NS_FILE_TALK,
);
$rtl = true;
$namespaceNames = array(
- NS_MEDIA => 'مدیا',
+ NS_MEDIA => 'مهدیا',
NS_SPECIAL => 'شا',
- NS_MAIN => '',
NS_TALK => 'گپ',
NS_USER => 'کارور',
NS_USER_TALK => 'کارور_گپ',
NS_PROJECT_TALK => '$1_گپ',
NS_FILE => 'پرونده',
NS_FILE_TALK => 'پرونده_گپ',
- NS_MEDIAWIKI => 'مدیاویکی',
- NS_MEDIAWIKI_TALK => 'مدیاویکی_گپ',
+ NS_MEDIAWIKI => 'مهدیاویکی',
+ NS_MEDIAWIKI_TALK => 'مهدیاویکی_گپ',
NS_TEMPLATE => 'شابلون',
NS_TEMPLATE_TALK => 'شابلون_گپ',
- NS_HELP => 'رانما',
- NS_HELP_TALK => 'راÙ\86Ù\85ا_گپ',
+ NS_HELP => 'راÙ\86Ù\87â\80\8cÙ\85ا',
+ NS_HELP_TALK => 'راÙ\86Ù\87â\80\8cÙ\85ائÙ\87_گپ',
NS_CATEGORY => 'رج',
NS_CATEGORY_TALK => 'رج_گپ',
);
$namespaceAliases = array(
- 'مهدیا' => NS_MEDIA,
'مدیا' => NS_MEDIA,
'ویژه' => NS_SPECIAL,
- 'بحث' => NS_TALK,
+ 'بحث' => NS_TALK,
'کاربر' => NS_USER,
- 'بحث_کاربر' => NS_USER_TALK,
- 'بحث_$1' => NS_PROJECT_TALK,
+ 'بحث_کاربر' => NS_USER_TALK,
+ 'بحث_$1' => NS_PROJECT_TALK,
'تصویر' => NS_FILE,
'پرونده' => NS_FILE,
- 'بحث_تصویر' => NS_FILE_TALK,
- 'بحث_پرونده' => NS_FILE_TALK,
+ 'بحث_تصویر' => NS_FILE_TALK,
+ 'بحث_پرونده' => NS_FILE_TALK,
'مدیاویکی' => NS_MEDIAWIKI,
'مهدیا ویکی' => NS_MEDIAWIKI,
- 'مهدیاویکی' => NS_MEDIAWIKI,
- 'مهدیاویکی_گپ' => NS_MEDIAWIKI_TALK,
- 'بحث_مدیاویکی' => NS_MEDIAWIKI_TALK,
+ 'بحث_مدیاویکی' => NS_MEDIAWIKI_TALK,
'مهدیا ویکی گپ' => NS_MEDIAWIKI_TALK,
'الگو' => NS_TEMPLATE,
- 'بحث_الگو' => NS_TEMPLATE_TALK,
+ 'بحث_الگو' => NS_TEMPLATE_TALK,
'راهنما' => NS_HELP,
- 'رانهما' => NS_HELP,
- 'رانهمائه_گپ' => NS_HELP_TALK,
- 'بحث_راهنما' => NS_HELP_TALK,
+ 'بحث_راهنما' => NS_HELP_TALK,
'رانهمای گپ' => NS_HELP_TALK,
'رده' => NS_CATEGORY,
- 'بحث_رده' => NS_CATEGORY_TALK,
+ 'بحث_رده' => NS_CATEGORY_TALK,
);
$magicWords = array(
$namespaceNames = array(
NS_MEDIA => 'Media',
- NS_SPECIAL => 'Spesiaal',
+ NS_SPECIAL => 'Speciaal',
NS_TALK => 'Overleg',
NS_USER => 'Gebruker',
NS_USER_TALK => 'Overleg_gebruker',
NS_PROJECT_TALK => 'Overleg_$1',
- NS_FILE => 'Bestaand',
- NS_FILE_TALK => 'Overleg_bestaand',
+ NS_FILE => 'Ofbeelding',
+ NS_FILE_TALK => 'Overleg_ofbeelding',
NS_MEDIAWIKI => 'MediaWiki',
NS_MEDIAWIKI_TALK => 'Overleg_MediaWiki',
NS_TEMPLATE => 'Mal',
NS_TEMPLATE_TALK => 'Overleg_mal',
NS_HELP => 'Hulpe',
NS_HELP_TALK => 'Overleg_hulpe',
- NS_CATEGORY => 'Kategorie',
- NS_CATEGORY_TALK => 'Overleg_kategorie',
+ NS_CATEGORY => 'Kattegerie',
+ NS_CATEGORY_TALK => 'Overleg_kattegerie',
);
$namespaceAliases = array(
- 'Speciaol' => NS_SPECIAL,
- 'Speciaal' => NS_SPECIAL,
- 'Sjabloon' => NS_TEMPLATE,
- 'Overleg_sjabloon' => NS_TEMPLATE_TALK,
- 'Ofbeelding' => NS_FILE,
- 'Overleg_ofbeelding' => NS_FILE_TALK,
- 'Categorie' => NS_CATEGORY,
- 'Overleg_categorie' => NS_CATEGORY_TALK,
- 'Kattegerie' => NS_CATEGORY,
- 'Overleg_categorie' => NS_CATEGORY_TALK,
- 'Overleg_kattegerie' => NS_HELP_TALK,
+ 'Speciaol' => NS_SPECIAL,
+ 'Sjabloon' => NS_TEMPLATE,
+ 'Overleg_sjabloon' => NS_TEMPLATE_TALK,
+ 'Categorie' => NS_CATEGORY,
+ 'Overleg_categorie' => NS_CATEGORY_TALK,
+ 'Overleg_help' => NS_HELP_TALK,
);
$dateFormats = array(
NS_MEDIA => 'ମାଧ୍ୟମ',
NS_SPECIAL => 'ବିଶେଷ',
NS_TALK => 'ଆଲୋଚନା',
- NS_USER => 'ବ୍ୟବହାରକାରୀ',
- NS_USER_TALK => 'ବ୍ୟବହାରକାରୀଙ୍କ_ଆଲୋଚନା',
- NS_PROJECT_TALK => '$1_ଆଲୋଚନା',
+ NS_USER => 'ବà\8dà\9fବାହାରà¬\95ାରà\80',
+ NS_USER_TALK => 'ବà\8dà\9fବାହାରà¬\95ାରà\80à¬\99à\8dà¬\95_à¬\86ଲà\8bà¬\9aନା',
+ NS_PROJECT_TALK => 'ଉଇକିପିଡ଼ିଆ_ଆଲୋଚନା',
NS_FILE => 'ଫାଇଲ',
NS_FILE_TALK => 'ଫାଇଲ_ଆଲୋଚନା',
NS_MEDIAWIKI => 'ମିଡ଼ିଆଉଇକି',
NS_MEDIAWIKI_TALK => 'ମିଡ଼ିଆଉଇକି_ଆଲୋଚନା',
- NS_TEMPLATE => 'à¬\9bାà¬\9eà\8dà¬\9a',
- NS_TEMPLATE_TALK => 'à¬\9bାà¬\9eà\8dà¬\9a_ଆଲୋଚନା',
- NS_HELP => 'ସହଯà\8bà¬\97',
- NS_HELP_TALK => 'ସହଯà\8bà¬\97_ଆଲୋଚନା',
+ NS_TEMPLATE => 'à¬\9fà\87ମà\8dପଲà\87à¬\9f',
+ NS_TEMPLATE_TALK => 'à¬\9fà\87ମà\8dପଲà\87à¬\9f_ଆଲୋଚନା',
+ NS_HELP => 'ସାହାଯà\8dà\9f',
+ NS_HELP_TALK => 'ସାହାଯà\8dà\9f_ଆଲୋଚନା',
NS_CATEGORY => 'ଶ୍ରେଣୀ',
NS_CATEGORY_TALK => 'ଶ୍ରେଣୀ_ଆଲୋଚନା',
);
$namespaceAliases = array(
'ବ୍ୟବହାରକାରି' => NS_USER,
'ବ୍ୟବହାରକାରିଁକ_ଆଲୋଚନା' => NS_USER_TALK,
- 'ବ୍ୟବାହାରକାରୀ' => NS_USER,
- 'ବ୍ୟବାହାରକାରୀଙ୍କ_ଆଲୋଚନା' => NS_USER_TALK,
- 'ଉଇକିପିଡ଼ିଆ_ଆଲୋଚନା' => NS_PROJECT_TALK,
'ଟେଁପଲେଟ' => NS_TEMPLATE,
'ଟେଁପଲେଟ_ଆଲୋଚନା' => NS_TEMPLATE_TALK,
- 'ଟେମ୍ପଲେଟ' => NS_TEMPLATE,
- 'ଟେମ୍ପଲେଟ_ଆଲୋଚନା' => NS_TEMPLATE_TALK,
'ବିଭାଗ' => NS_CATEGORY,
'ବିଭାଗିୟ_ଆଲୋଚନା' => NS_CATEGORY_TALK,
- 'ସାହାଯ୍ୟ' => NS_HELP,
- 'ସାହାଯ୍ୟ_ଆଲୋଚନା' => NS_HELP_TALK,
);
$specialPageAliases = array(
$fallback = 'ru';
$namespaceNames = array(
- NS_MEDIA => 'Медиа',
+ NS_MEDIA => 'Media',
NS_SPECIAL => 'Сæрмагонд',
NS_TALK => 'Тæрхон',
NS_USER => 'Архайæг',
NS_USER_TALK => 'Архайæджы_ныхас',
- NS_PROJECT_TALK => '{{GRAMMAR:genitive|$1}}_тæрхон',
- NS_FILE => 'Файл',
- NS_FILE_TALK => 'Файлы_тæрхон',
+ NS_PROJECT_TALK => 'Дискусси_$1',
+ NS_FILE => 'Ð\9dÑ\8bв',
+ NS_FILE_TALK => 'Ð\9dÑ\8bвы_тæрхон',
NS_MEDIAWIKI => 'MediaWiki',
- NS_MEDIAWIKI_TALK => 'MediaWiki-йы_тæрхон',
+ NS_MEDIAWIKI_TALK => 'Тæрхон_MediaWiki',
NS_TEMPLATE => 'Шаблон',
NS_TEMPLATE_TALK => 'Шаблоны_тæрхон',
NS_HELP => 'Æххуыс',
$namespaceAliases = array(
'Дискусси' => NS_TALK,
'Архайæджы_дискусси' => NS_USER_TALK,
- 'Дискусси_$1' => NS_PROJECT_TALK,
- 'Ныв' => NS_FILE,
- 'Нывы_тæрхон' => NS_FILE_TALK,
'Нывы_тыххæй_дискусси' => NS_FILE_TALK,
'Дискусси_MediaWiki' => NS_MEDIAWIKI_TALK,
- 'Тæрхон_MediaWiki' => NS_MEDIAWIKI_TALK,
'Шаблоны_тыххæй_дискусси' => NS_TEMPLATE_TALK,
'Æххуысы_тыххæй_дискусси' => NS_HELP_TALK,
'Категорийы_тыххæй_дискусси' => NS_CATEGORY_TALK,
);
-// Remove Russian aliases
-$namespaceGenderAliases = array();
$magicWords = array(
'redirect' => array( '0', '#РАРВЫСТ', '#перенаправление', '#перенапр', '#REDIRECT' ),
$fallback = 'qu, es';
-$namespaceNames = array(
- NS_MEDIA => 'Midya',
- NS_SPECIAL => 'Sapak',
- NS_TALK => 'Rimanakuy',
- NS_USER => 'Rurak',
- NS_USER_TALK => 'Rurakpa_rimanakuy',
- NS_PROJECT_TALK => '$1-pa_rimanakuy',
- NS_FILE => 'Rikcha',
- NS_FILE_TALK => 'Rikchapa_rimanakuy',
- NS_MEDIAWIKI => 'MediaWiki',
- NS_MEDIAWIKI_TALK => 'MediaWikipa_rimanakuy',
- NS_TEMPLATE => 'Plantilla',
- NS_TEMPLATE_TALK => 'Plantillapa_rimanakuy',
- NS_HELP => 'Yanapa',
- NS_HELP_TALK => 'Yanapapak_rimanakuy',
- NS_CATEGORY => 'Samiyachiy',
- NS_CATEGORY_TALK => 'Samiyachiy_rimanakuy',
-);
-
$messages = array(
# User preference toggles
'tog-underline' => 'Tinkikunana uraypi aspishpa rikuchina',
$namespaceNames = array(
NS_MEDIA => 'माध्यमम्',
- NS_SPECIAL => 'विशेषम्',
+ NS_SPECIAL => 'विशेष',
NS_TALK => 'सम्भाषणम्',
NS_USER => 'योजकः',
NS_USER_TALK => 'योजकसम्भाषणम्',
$namespaceAliases = array(
'माध्यम' => NS_MEDIA,
- 'विशेष' => NS_SPECIAL,
'संभाषणं' => NS_TALK,
'योजकसंभाषणं' => NS_USER_TALK,
'$1संभाषणं' => NS_PROJECT_TALK,
NS_USER => 'Корисник',
NS_USER_TALK => 'Разговор_са_корисником',
NS_PROJECT_TALK => 'Разговор_о_$1',
- NS_FILE => 'ФаÑ\98л',
- NS_FILE_TALK => 'Разговор_о_фајлу',
- NS_MEDIAWIKI => 'Ð\9cедиÑ\98аÐ\92ики',
- NS_MEDIAWIKI_TALK => 'РазговоÑ\80_о_Ð\9cедиÑ\98аÐ\92икију',
+ NS_FILE => 'Ð\94аÑ\82оÑ\82ека',
+ NS_FILE_TALK => 'Разговор_о_датотеци',
+ NS_MEDIAWIKI => 'Ð\9cедиÑ\98авики',
+ NS_MEDIAWIKI_TALK => 'РазговоÑ\80_о_Ð\9cедиÑ\98авикију',
NS_TEMPLATE => 'Шаблон',
NS_TEMPLATE_TALK => 'Разговор_о_шаблону',
NS_HELP => 'Помоћ',
'Слика' => NS_FILE,
'Разговор_о_слици' => NS_FILE_TALK,
- "Датотека" => NS_FILE,
- "Разговор_о_датотеци" => NS_FILE_TALK,
- "Медијавики" => NS_MEDIAWIKI,
- "Разговор_о_Медијавикију" => NS_MEDIAWIKI_TALK,
'МедијаВики' => NS_MEDIAWIKI,
'Разговор_о_МедијаВикију' => NS_MEDIAWIKI_TALK,
);
* @author לערי ריינהארט
*/
-$namespaceNames = array(
- NS_MEDIA => 'Medya',
- NS_SPECIAL => 'Pinaurog',
- NS_TALK => 'Hiruhimangraw',
- NS_USER => 'Gumaramit',
- NS_USER_TALK => 'Hiruhimangaw_hiton_gumaramit',
- NS_PROJECT_TALK => 'Hiruhimangraw_hiton_$1',
- NS_FILE => 'Fayl',
- NS_FILE_TALK => 'Hiruhimangraw_hiton_fayl',
- NS_MEDIAWIKI => 'MediaWiki',
- NS_MEDIAWIKI_TALK => 'Hiruhimangraw_hiton_MediaWiki',
- NS_TEMPLATE => 'Batakan',
- NS_TEMPLATE_TALK => 'Hiruhimangraw_hiton_batakan',
- NS_HELP => 'Bulig',
- NS_HELP_TALK => 'Hiruhimangaw_hiton_bulig',
- NS_CATEGORY => 'Kaarangay',
- NS_CATEGORY_TALK => 'Hiruhimangraw_hiton_kaarangay',
-);
-
$specialPageAliases = array(
'Allpages' => array( 'NgatananngaPakli' ),
'Categories' => array( 'Mga_kaarangay' ),
NS_TALK => '傾偈',
NS_USER => '用戶',
NS_USER_TALK => '用戶傾偈',
- NS_PROJECT_TALK => '$1傾偈',
+ NS_PROJECT_TALK => '$1_傾偈',
NS_FILE => '文件',
NS_FILE_TALK => '文件傾偈',
NS_MEDIAWIKI => 'MediaWiki',
- NS_MEDIAWIKI_TALK => 'MediaWiki傾偈',
+ NS_MEDIAWIKI_TALK => 'MediaWiki_傾偈',
NS_TEMPLATE => '模',
NS_TEMPLATE_TALK => '模傾偈',
NS_HELP => '幫手',
"用户 对话" => NS_USER_TALK,
"用戶 討論" => NS_USER_TALK,
"用户 讨论" => NS_USER_TALK,
- '$1_傾偈' => NS_PROJECT_TALK,
+ # This has never worked so it's unlikely to annoy anyone if I disable it -- TS
+ # "{$wgMetaNamespace} 討論" => NS_PROJECT_TALK,
+ # "{$wgMetaNamespace} 讨论" => NS_PROJECT_TALK,
"檔" => NS_FILE,
"檔案" => NS_FILE,
"档" => NS_FILE,
"图 讨论" => NS_FILE_TALK,
"圖像 討論" => NS_FILE_TALK,
"图像 讨论" => NS_FILE_TALK,
- 'MediaWiki_傾偈' => NS_FILE_TALK,
"模 討論" => NS_TEMPLATE_TALK,
"模 讨论" => NS_TEMPLATE_TALK,
"幫助" => NS_HELP,
+++ /dev/null
--- File backend operation journal
-CREATE TABLE /*_*/filejournal (
- -- Unique ID for each file operation
- fj_id bigint unsigned NOT NULL PRIMARY KEY auto_increment,
- -- UUID of the batch this operation belongs to
- fj_batch_uuid varbinary(32) NOT NULL,
- -- The registered file backend name
- fj_backend varchar(255) NOT NULL,
- -- The storage path that was affected (may be internal paths)
- fj_path blob NOT NULL,
- -- SHA-1 file path hash in base-36
- fj_path_sha1 varbinary(32) NOT NULL default '',
- -- Primitive operation description (create/update/delete)
- fj_op varchar(16) NOT NULL default '',
- -- SHA-1 file content hash in base-36
- fj_new_sha1 varbinary(32) NOT NULL default '',
- -- Timestamp of the batch operation
- fj_timestamp varbinary(14) NOT NULL default ''
-);
-
-CREATE INDEX /*i*/fj_batch_id ON /*_*/filejournal (fj_batch_uuid,fj_id);
-CREATE INDEX /*i*/fj_path_id ON /*_*/filejournal (fj_path_sha1,fj_id);
-CREATE INDEX /*i*/fj_new_sha1 ON /*_*/filejournal (fj_new_sha1,fj_id);
-CREATE INDEX /*i*/fj_timestamp ON /*_*/filejournal (fj_timestamp);
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
*/
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;
$this->initProgress( $this->history );
- // 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->db = $this->backupDb();
$this->egress = new ExportProgressFilter( $this->sink, $this );
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++;
-
- // 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
+ if ( isset( $this->prefetch ) ) {
+ $text = $this->prefetch->prefetch( $this->thisPage, $this->thisRev );
+ if ( $text !== null ) { // Entry missing from prefetch dump
+ $dbr = wfGetDB( DB_SLAVE );
$revID = intval( $this->thisRev );
- if ( ! isset( $this->db ) ) {
- throw new MWException( "No database available" );
- }
- $revLength = $this->db->selectField( 'revision', 'rev_len', array( 'rev_id' => $revID ) );
+ $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 ) {
- if ( $tryIsPrefetch ) {
- $this->prefetchCount++;
- }
+ $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 );
}
-
- // Something went wrong; we did not a text that was plausible :(
- $failures++;
-
+ }
+ return $this->doGetText( $id );
+ }
- // 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 ) {
+ 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
$this->closeSpawn();
$this->openSpawn();
}
- } catch (Exception $e) {
- $this->progress( "Rebooting getText infrastructure failed (".$e->getMessage().")" .
- " Trying to continue anyways" );
+ $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;
}
}
+ return '';
+ }
- // 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" );
+ /**
+ * 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;
}
-
- 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 ),
'customjsprotected',
'ns-specialprotected',
'titleprotected',
- 'filereadonlyerror'
),
'virus' => array(
'virus-badscanner',
'backend-fail-batchsize'
),
- 'filejournal-errors' => array(
- 'filejournal-fail-dbconnect',
- 'filejournal-fail-dbquery'
- ),
-
'lockmanager-errors' => array(
'lockmanager-notlocked',
'lockmanager-fail-closelock',
*/
( 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 ) {
- 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 );
+/**
+ * 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 ),
$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 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;
- }
+/**
+ * @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;
+ }
+ 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';
}
- $( 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' );
+ return 'view';
+}
- // Allowing people to add inline animated links is a little scary
- $links = $links.filter( ':not( #bodyContent *, #content * )' );
+$( 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' );
- $links.click( function ( e ) {
- var action, api, $link;
+ // Allowing people to add inline animated links is a little scary
+ $links = $links.filter( ':not( #bodyContent *, #content * )' );
+ $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 $li, otherAction;
-
- otherAction = action === 'watch' ? 'unwatch' : 'watch';
+ 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' );
- 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' );
+ 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(){
+
+ // 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' );
+
+ }
+ );
});
-}( jQuery, mediaWiki ) );
+});
+
+})( jQuery, mediaWiki );
*/
( 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 properties 'title' (full pagename), 'watched' (boolean) and
+ * @param success {Function} callback to which the watch object will be passed
+ * watch object contains 'title' (full page name), 'watched' (boolean) and
* 'message' (parsed HTML of the 'addedwatchtext' message).
- * @param err {Function} Error callback (optional)
+ * @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)
* @return {jqXHR}
*/
- watch: function ( page, success, err ) {
- return doWatchInternal.call( this, page, success, err );
+ 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 } );
},
/**
* 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 properties 'title' (full pagename), 'watched' (boolean) and
+ * @param success {Function} callback to which the watch object will be passed
+ * watch object contains 'title' (full page name), 'unwatched' (boolean) and
* 'message' (parsed HTML of the 'removedwatchtext' message).
- * @param err {Function} Error callback (optional)
+ * @param err {Function} callback if error (optional)
* @return {jqXHR}
*/
- unwatch: function ( page, success, err ) {
- return doWatchInternal.call( this, page, success, err, { unwatch: 1 } );
+ unwatch: function( page, success, err ) {
+ return this.watch( page, success, err, true );
}
} );