* Ebrahim Byagowi
* Edward Z. Yang
* Elvis Stansvik
+* Eranroz
* Erwin Dokter
* Federico Leva
* FunPika
* (bug 56199) Raw option of parser functions must now match complete word,
to take effect.
* (bug 60543) Special:PrefixIndex forgot stripprefix=1 for "Next page" link
+* (bug 29762) Undoing an already-undone edit will now display an appropriate
+ message instead of leading the user to make a null edit.
=== Web API changes in 1.23 ===
* (bug 54884) action=parse&prop=categories now indicates hidden and missing
list=usercontribs.
* list=watchlist now uses the querying user's rights rather than the wlowner's
rights when checking whether wlprop=patrol is allowed.
+* (bug 32151) ApiWatch now has pageset capabilities (titles/pageids/generators).
+ Title parameter is now deprecated.
+* (bug 23005) Added action=revisiondelete.
=== Languages updated in 1.23 ===
}
if ( !$wgCachePages ) {
- wfDebug( "$fname: CACHE DISABLED\n", false );
+ wfDebug( "$fname: CACHE DISABLED\n", 'log' );
return false;
}
$modsince = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] );
$modsinceTime = strtotime( $modsince );
$ismodsince = wfTimestamp( TS_MW, $modsinceTime ? $modsinceTime : 1 );
- wfDebug( "$fname: -- client send If-Modified-Since: " . $modsince . "\n", false );
- wfDebug( "$fname: -- we might send Last-Modified : $lastmod\n", false );
+ wfDebug( "$fname: -- client send If-Modified-Since: " . $modsince . "\n", 'log' );
+ wfDebug( "$fname: -- we might send Last-Modified : $lastmod\n", 'log' );
if ( ( $ismodsince >= $timestamp ) && $wgUser->validateCache( $ismodsince ) && $ismodsince >= $wgCacheEpoch ) {
ini_set( 'zlib.output_compression', 0 );
$this->disable();
$this->mLastModified = $lastmod;
- wfDebug( "$fname: CACHED client: $ismodsince ; user: {$wgUser->getTouched()} ; page: $timestamp ; site $wgCacheEpoch\n", false );
+ wfDebug( "$fname: CACHED client: $ismodsince ; user: {$wgUser->getTouched()} ; page: $timestamp ; site $wgCacheEpoch\n", 'log' );
return true;
} else {
- wfDebug( "$fname: READY client: $ismodsince ; user: {$wgUser->getTouched()} ; page: $timestamp ; site $wgCacheEpoch\n", false );
+ wfDebug( "$fname: READY client: $ismodsince ; user: {$wgUser->getTouched()} ; page: $timestamp ; site $wgCacheEpoch\n", 'log' );
$this->mLastModified = $lastmod;
}
} else {
- wfDebug( "$fname: client did not send If-Modified-Since header\n", false );
+ wfDebug( "$fname: client did not send If-Modified-Since header\n", 'log' );
$this->mLastModified = $lastmod;
}
return false;
'ApiQueryWatchlist' => 'includes/api/ApiQueryWatchlist.php',
'ApiQueryWatchlistRaw' => 'includes/api/ApiQueryWatchlistRaw.php',
'ApiResult' => 'includes/api/ApiResult.php',
+ 'ApiRevisionDelete' => 'includes/api/ApiRevisionDelete.php',
'ApiRollback' => 'includes/api/ApiRollback.php',
'ApiRsd' => 'includes/api/ApiRsd.php',
'ApiSetNotificationTimestamp' => 'includes/api/ApiSetNotificationTimestamp.php',
* Expensive Querypages are already updated.
*/
$wgSpecialPageCacheUpdates = array(
- 'Statistics' => array( 'SiteStatsUpdate', 'cacheUpdate' )
+ 'Statistics' => array( 'SiteStatsUpdate', 'cacheUpdate' ),
+ 'Activeusers' => array( 'SpecialActiveUsers', 'cacheUpdate' ),
);
/**
* @since 1.21
*/
protected function getContentObject( $def_content = null ) {
- global $wgOut, $wgRequest;
+ global $wgOut, $wgRequest, $wgUser, $wgContLang;
wfProfileIn( __METHOD__ );
# Warn the user that something went wrong
$undoMsg = 'failure';
} else {
- # Inform the user of our success and set an automatic edit summary
- $undoMsg = 'success';
-
- # If we just undid one rev, use an autosummary
- $firstrev = $oldrev->getNext();
- if ( $firstrev && $firstrev->getId() == $undo ) {
- $userText = $undorev->getUserText();
- if ( $userText === '' ) {
- $undoSummary = wfMessage(
- 'undo-summary-username-hidden',
- $undo
- )->inContentLanguage()->text();
- } else {
- $undoSummary = wfMessage(
- 'undo-summary',
- $undo,
- $userText
- )->inContentLanguage()->text();
+ $oldContent = $this->mArticle->getPage()->getContent( Revision::RAW );
+ $popts = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang );
+ $newContent = $content->preSaveTransform( $this->mTitle, $wgUser, $popts );
+
+ if ( $newContent->equals( $oldContent ) ) {
+ # Tell the user that the undo results in no change,
+ # i.e. the revisions were already undone.
+ $undoMsg = 'nochange';
+ $content = false;
+ } else {
+ # Inform the user of our success and set an automatic edit summary
+ $undoMsg = 'success';
+
+ # If we just undid one rev, use an autosummary
+ $firstrev = $oldrev->getNext();
+ if ( $firstrev && $firstrev->getId() == $undo ) {
+ $userText = $undorev->getUserText();
+ if ( $userText === '' ) {
+ $undoSummary = wfMessage(
+ 'undo-summary-username-hidden',
+ $undo
+ )->inContentLanguage()->text();
+ } else {
+ $undoSummary = wfMessage(
+ 'undo-summary',
+ $undo,
+ $userText
+ )->inContentLanguage()->text();
+ }
+ if ( $this->summary === '' ) {
+ $this->summary = $undoSummary;
+ } else {
+ $this->summary = $undoSummary . wfMessage( 'colon-separator' )
+ ->inContentLanguage()->text() . $this->summary;
+ }
+ $this->undidRev = $undo;
}
- if ( $this->summary === '' ) {
- $this->summary = $undoSummary;
- } else {
- $this->summary = $undoSummary . wfMessage( 'colon-separator' )
- ->inContentLanguage()->text() . $this->summary;
- }
- $this->undidRev = $undo;
+ $this->formtype = 'diff';
}
- $this->formtype = 'diff';
}
} else {
// Failed basic sanity checks.
$undoMsg = 'norev';
}
- // Messages: undo-success, undo-failure, undo-norev
+ // Messages: undo-success, undo-failure, undo-norev, undo-nochange
$class = ( $undoMsg == 'success' ? '' : 'error ' ) . "mw-undo-{$undoMsg}";
$this->editFormPageTop .= $wgOut->parse( "<div class=\"{$class}\">" .
wfMessage( 'undo-' . $undoMsg )->plain() . '</div>', true, /* interface */true );
if ( !( $e instanceof MWException ) || $e->isLoggable() ) {
$log = self::getLogMessage( $e );
if ( $wgLogExceptionBacktrace ) {
- wfDebugLog( 'exception', $log . "\n" . $e->getTraceAsString() . "\n" );
+ wfDebugLog( 'exception', $log . "\n" . $e->getTraceAsString() );
} else {
wfDebugLog( 'exception', $log );
}
$json = self::jsonSerializeException( $e, false, FormatJson::ALL_OK );
if ( $json !== false ) {
- wfDebugLog( 'exception-json', $json, false );
+ wfDebugLog( 'exception-json', $json, 'private' );
}
}
* $wgDebugComments - if on, some debug items may appear in comments in the HTML output.
*
* @param $text String
- * @param bool $logonly set true to avoid appearing in HTML when $wgDebugComments is set
- */
-function wfDebug( $text, $logonly = false ) {
+ * @param string|bool $dest Destination of the message:
+ * - 'all': both to the log and HTML (debug toolbar or HTML comments)
+ * - 'log': only to the log and not in HTML
+ * For backward compatibility, it can also take a boolean:
+ * - true: same as 'all'
+ * - false: same as 'log'
+ */
+function wfDebug( $text, $dest = 'all' ) {
global $wgDebugLogFile, $wgProfileOnly, $wgDebugRawPage, $wgDebugLogPrefix;
if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
return;
}
+ // Turn $dest into a string if it's a boolean (for b/c)
+ if ( $dest === true ) {
+ $dest = 'all';
+ } elseif ( $dest === false ) {
+ $dest = 'log';
+ }
+
$timer = wfDebugTimer();
if ( $timer !== '' ) {
$text = preg_replace( '/[^\n]/', $timer . '\0', $text, 1 );
}
- if ( !$logonly ) {
+ if ( $dest === 'all' ) {
MWDebug::debugMsg( $text );
}
* @param $text String
* @param bool $public whether to log the event in the public log if no private
* log file is specified, (default true)
- */
-function wfDebugLog( $logGroup, $text, $public = true ) {
+ * @param string|bool $dest Destination of the message:
+ * - 'all': both to the log and HTML (debug toolbar or HTML comments)
+ * - 'log': only to the log and not in HTML
+ * - 'private': only to the specifc log if set in $wgDebugLogGroups and
+ * discarded otherwise
+ * For backward compatibility, it can also take a boolean:
+ * - true: same as 'all'
+ * - false: same as 'private'
+ */
+function wfDebugLog( $logGroup, $text, $dest = 'all' ) {
global $wgDebugLogGroups;
+
$text = trim( $text ) . "\n";
+ // Turn $dest into a string if it's a boolean (for b/c)
+ if ( $dest === true ) {
+ $dest = 'all';
+ } elseif ( $dest === false ) {
+ $dest = 'private';
+ }
+
if ( !isset( $wgDebugLogGroups[$logGroup] ) ) {
- if ( $public === true ) {
- wfDebug( "[$logGroup] $text", false );
+ if ( $dest !== 'private' ) {
+ wfDebug( "[$logGroup] $text", $dest );
}
return;
}
+ if ( $dest === 'all' ) {
+ MWDebug::debugMsg( "[$logGroup] $text" );
+ }
+
$logConfig = $wgDebugLogGroups[$logGroup];
if ( is_array( $logConfig ) ) {
if ( isset( $logConfig['sample'] ) && mt_rand( 1, $logConfig['sample'] ) !== 1 ) {
$pipes = null;
$proc = proc_open( $cmd, $desc, $pipes );
if ( !$proc ) {
- wfDebugLog( 'exec', "proc_open() failed: $cmd\n" );
+ wfDebugLog( 'exec', "proc_open() failed: $cmd" );
$retval = -1;
return '';
}
}
if ( $logMsg !== false ) {
- wfDebugLog( 'exec', "$logMsg: $cmd\n" );
+ wfDebugLog( 'exec', "$logMsg: $cmd" );
}
return $outBuffer;
return false;
}
if ( !$wgCachePages ) {
- wfDebug( __METHOD__ . ": CACHE DISABLED\n", false );
+ wfDebug( __METHOD__ . ": CACHE DISABLED\n", 'log' );
return false;
}
$clientHeader = $this->getRequest()->getHeader( 'If-Modified-Since' );
if ( $clientHeader === false ) {
- wfDebug( __METHOD__ . ": client did not send If-Modified-Since header\n", false );
+ wfDebug( __METHOD__ . ": client did not send If-Modified-Since header\n", 'log' );
return false;
}
}
wfDebug( __METHOD__ . ": client sent If-Modified-Since: " .
- wfTimestamp( TS_ISO_8601, $clientHeaderTime ) . "\n", false );
+ wfTimestamp( TS_ISO_8601, $clientHeaderTime ) . "\n", 'log' );
wfDebug( __METHOD__ . ": effective Last-Modified: " .
- wfTimestamp( TS_ISO_8601, $maxModified ) . "\n", false );
+ wfTimestamp( TS_ISO_8601, $maxModified ) . "\n", 'log' );
if ( $clientHeaderTime < $maxModified ) {
- wfDebug( __METHOD__ . ": STALE, $info\n", false );
+ wfDebug( __METHOD__ . ": STALE, $info\n", 'log' );
return false;
}
# Not modified
# Give a 304 response code and disable body output
- wfDebug( __METHOD__ . ": NOT MODIFIED, $info\n", false );
+ wfDebug( __METHOD__ . ": NOT MODIFIED, $info\n", 'log' );
ini_set( 'zlib.output_compression', 0 );
$this->getRequest()->response()->header( "HTTP/1.1 304 Not Modified" );
$this->sendCacheControl();
# We'll purge the proxy cache explicitly, but require end user agents
# to revalidate against the proxy on each visit.
# Surrogate-Control controls our Squid, Cache-Control downstream caches
- wfDebug( __METHOD__ . ": proxy caching with ESI; {$this->mLastModified} **\n", false );
+ wfDebug( __METHOD__ . ": proxy caching with ESI; {$this->mLastModified} **\n", 'log' );
# start with a shorter timeout for initial testing
# header( 'Surrogate-Control: max-age=2678400+2678400, content="ESI/1.0"');
$response->header( 'Surrogate-Control: max-age=' . $wgSquidMaxage . '+' . $this->mSquidMaxage . ', content="ESI/1.0"' );
# to revalidate against the proxy on each visit.
# IMPORTANT! The Squid needs to replace the Cache-Control header with
# Cache-Control: s-maxage=0, must-revalidate, max-age=0
- wfDebug( __METHOD__ . ": local proxy caching; {$this->mLastModified} **\n", false );
+ wfDebug( __METHOD__ . ": local proxy caching; {$this->mLastModified} **\n", 'log' );
# start with a shorter timeout for initial testing
# header( "Cache-Control: s-maxage=2678400, must-revalidate, max-age=0" );
$response->header( 'Cache-Control: s-maxage=' . $this->mSquidMaxage . ', must-revalidate, max-age=0' );
} else {
# We do want clients to cache if they can, but they *must* check for updates
# on revisiting the page.
- wfDebug( __METHOD__ . ": private caching; {$this->mLastModified} **\n", false );
+ wfDebug( __METHOD__ . ": private caching; {$this->mLastModified} **\n", 'log' );
$response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
$response->header( "Cache-Control: private, must-revalidate, max-age=0" );
}
$response->header( "Last-Modified: {$this->mLastModified}" );
}
} else {
- wfDebug( __METHOD__ . ": no caching **\n", false );
+ wfDebug( __METHOD__ . ": no caching **\n", 'log' );
# In general, the absence of a last modified header should be enough to prevent
# the client from using its cache. We send a few other things just to make sure.
'emailaddress',
);
+ /**
+ * @return array
+ */
+ static function getSaveBlacklist() {
+ return self::$saveBlacklist;
+ }
+
/**
* @throws MWException
* @param $user User
* @param $msg string
*/
protected function log( $msg ) {
- wfDebugLog( 'squid', __CLASS__ . " ($this->host): $msg\n" );
+ wfDebugLog( 'squid', __CLASS__ . " ($this->host): $msg" );
}
}
$ipList = gethostbynamel( $host );
if ( $ipList ) {
- wfDebugLog( 'dnsblacklist', "Hostname $host is {$ipList[0]}, it's a proxy says $base!\n" );
+ wfDebugLog( 'dnsblacklist', "Hostname $host is {$ipList[0]}, it's a proxy says $base!" );
$found = true;
break;
} else {
- wfDebugLog( 'dnsblacklist', "Requested $host, not found in $base.\n" );
+ wfDebugLog( 'dnsblacklist', "Requested $host, not found in $base." );
}
}
}
* - 'registered-checkmatrix' - as above, using the 'checkmatrix' type.
* - 'userjs' - preferences with names starting with 'userjs-', intended to
* be used by user scripts.
+ * - 'special' - "preferences" that are not accessible via User::getOptions
+ * or User::setOptions.
* - 'unused' - preferences about which MediaWiki doesn't know anything.
* These are usually legacy options, removed in newer versions.
*
'registered-multiselect',
'registered-checkmatrix',
'userjs',
+ 'special',
'unused'
);
}
$prefs = Preferences::getPreferences( $this, $context );
$mapping = array();
+ // Pull out the "special" options, so they don't get converted as
+ // multiselect or checkmatrix.
+ $specialOptions = array_fill_keys( Preferences::getSaveBlacklist(), true );
+ foreach ( $specialOptions as $name => $value ) {
+ unset( $prefs[$name] );
+ }
+
// Multiselect and checkmatrix options are stored in the database with
// one key per option, each having a boolean value. Extract those keys.
$multiselectOptions = array();
$mapping[$key] = 'registered-multiselect';
} elseif ( isset( $checkmatrixOptions[$key] ) ) {
$mapping[$key] = 'registered-checkmatrix';
+ } elseif ( isset( $specialOptions[$key] ) ) {
+ $mapping[$key] = 'special';
} elseif ( substr( $key, 0, 7 ) === 'userjs-' ) {
$mapping[$key] = 'userjs';
} else {
$this->parserOutput = ParserCache::singleton()->getDirty( $this->page, $this->parserOptions );
if ( $this->parserOutput === false ) {
- wfDebugLog( 'dirty', "dirty missing\n" );
+ wfDebugLog( 'dirty', 'dirty missing' );
wfDebug( __METHOD__ . ": no dirty cache\n" );
return false;
} else {
wfDebug( __METHOD__ . ": sending dirty output\n" );
- wfDebugLog( 'dirty', "dirty output {$this->cacheKey}\n" );
+ wfDebugLog( 'dirty', "dirty output {$this->cacheKey}" );
$this->isDirty = true;
return true;
}
}
/**
- * Throw a UsageException based on the errors in the Status object.
+ * Get error (as code, string) from a Status object.
*
- * @since 1.22
+ * @since 1.23
* @param Status $status Status object
- * @throws MWException
+ * @return array of code and error string
*/
- public function dieStatus( $status ) {
+ public function getErrorFromStatus( $status ) {
if ( $status->isGood() ) {
throw new MWException( 'Successful status passed to ApiBase::dieStatus' );
}
// Translate message to code, for backwards compatability
$code = ApiBase::$messageMap[$code]['code'];
}
- $this->dieUsage( $msg->inLanguage( 'en' )->useDatabase( false )->plain(), $code );
+ return array( $code, $msg->inLanguage( 'en' )->useDatabase( false )->plain() );
+ }
+
+ /**
+ * Throw a UsageException based on the errors in the Status object.
+ *
+ * @since 1.22
+ * @param Status $status Status object
+ * @throws MWException
+ */
+ public function dieStatus( $status ) {
+
+ list( $code, $msg ) = $this->getErrorFromStatus( $status );
+ $this->dieUsage( $msg, $code );
}
// @codingStandardsIgnoreStart Allow long lines. Cannot split these.
* @ingroup API
*/
class ApiMain extends ApiBase {
-
/**
* When no format parameter is given, this format will be used
*/
'userrights' => 'ApiUserrights',
'options' => 'ApiOptions',
'imagerotate' => 'ApiImageRotate',
+ 'revisiondelete' => 'ApiRevisionDelete',
);
/**
}
}
$s .= "\n";
- wfDebugLog( 'api', $s, false );
+ wfDebugLog( 'api', $s, 'private' );
}
/**
array( 'code' => 'maxlag', 'info' => 'Waiting for host: x seconds lagged' ),
array( 'code' => 'maxlag', 'info' => 'Waiting for a database server: x seconds lagged' ),
array( 'code' => 'assertuserfailed', 'info' => 'Assertion that the user is logged in failed' ),
- array( 'code' => 'assertbotfailed', 'info' => 'Assertion that the user has the bot right failed' ),
+ array(
+ 'code' => 'assertbotfailed',
+ 'info' => 'Assertion that the user has the bot right failed'
+ ),
) );
}
$validation = true;
}
break;
+ case 'special':
+ $validation = "cannot be set by this module";
+ break;
case 'unused':
default:
$validation = "not a valid preference";
* @since 1.21 derives from ApiBase instead of ApiQueryBase
*/
class ApiPageSet extends ApiBase {
-
/**
* Constructor flag: The new instance of ApiPageSet will ignore the 'generator=' parameter
* @since 1.21
*/
private $mDefaultNamespace = NS_MAIN;
+ /**
+ * Add all items from $values into the result
+ * @param array $result output
+ * @param array $values values to add
+ * @param string $flag the name of the boolean flag to mark this element
+ * @param string $name if given, name of the value
+ */
+ private static function addValues( array &$result, $values, $flag = null, $name = null ) {
+ foreach ( $values as $val ) {
+ if ( $val instanceof Title ) {
+ $v = array();
+ ApiQueryBase::addTitleInfo( $v, $val );
+ } elseif ( $name !== null ) {
+ $v = array( $name => $val );
+ } else {
+ $v = $val;
+ }
+ if ( $flag !== null ) {
+ $v[$flag] = '';
+ }
+ $result[] = $v;
+ }
+ }
+
/**
* Constructor
* @param $dbSource ApiBase Module implementing getDB().
return $values;
}
+ /**
+ * Get an array of invalid/special/missing titles.
+ *
+ * @param $invalidChecks List of types of invalid titles to include.
+ * Recognized values are:
+ * - invalidTitles: Titles from $this->getInvalidTitles()
+ * - special: Titles from $this->getSpecialTitles()
+ * - missingIds: ids from $this->getMissingPageIDs()
+ * - missingRevIds: ids from $this->getMissingRevisionIDs()
+ * - missingTitles: Titles from $this->getMissingTitles()
+ * - interwikiTitles: Titles from $this->getInterwikiTitlesAsResult()
+ * @return array Array suitable for inclusion in the response
+ * @since 1.23
+ */
+ public function getInvalidTitlesAndRevisions( $invalidChecks = array( 'invalidTitles',
+ 'special', 'missingIds', 'missingRevIds', 'missingTitles', 'interwikiTitles' )
+ ) {
+ $result = array();
+ if ( in_array( "invalidTitles", $invalidChecks ) ) {
+ self::addValues( $result, $this->getInvalidTitles(), 'invalid', 'title' );
+ }
+ if ( in_array( "special", $invalidChecks ) ) {
+ self::addValues( $result, $this->getSpecialTitles(), 'special', 'title' );
+ }
+ if ( in_array( "missingIds", $invalidChecks ) ) {
+ self::addValues( $result, $this->getMissingPageIDs(), 'missing', 'pageid' );
+ }
+ if ( in_array( "missingRevIds", $invalidChecks ) ) {
+ self::addValues( $result, $this->getMissingRevisionIDs(), 'missing', 'revid' );
+ }
+ if ( in_array( "missingTitles", $invalidChecks ) ) {
+ self::addValues( $result, $this->getMissingTitles(), 'missing' );
+ }
+ if ( in_array( "interwikiTitles", $invalidChecks ) ) {
+ self::addValues( $result, $this->getInterwikiTitlesAsResult() );
+ }
+ return $result;
+ }
+
/**
* Get the list of revision IDs (requested with the revids= parameter)
* @return array revID (int) => pageID (int)
class ApiPurge extends ApiBase {
private $mPageSet;
- /**
- * Add all items from $values into the result
- * @param array $result output
- * @param array $values values to add
- * @param string $flag the name of the boolean flag to mark this element
- * @param string $name if given, name of the value
- */
- private static function addValues( array &$result, $values, $flag = null, $name = null ) {
- foreach ( $values as $val ) {
- if ( $val instanceof Title ) {
- $v = array();
- ApiQueryBase::addTitleInfo( $v, $val );
- } elseif ( $name !== null ) {
- $v = array( $name => $val );
- } else {
- $v = $val;
- }
- if ( $flag !== null ) {
- $v[$flag] = '';
- }
- $result[] = $v;
- }
- }
-
/**
* Purges the cache of a page
*/
$pageSet = $this->getPageSet();
$pageSet->execute();
- $result = array();
- self::addValues( $result, $pageSet->getInvalidTitles(), 'invalid', 'title' );
- self::addValues( $result, $pageSet->getSpecialTitles(), 'special', 'title' );
- self::addValues( $result, $pageSet->getMissingPageIDs(), 'missing', 'pageid' );
- self::addValues( $result, $pageSet->getMissingRevisionIDs(), 'missing', 'revid' );
- self::addValues( $result, $pageSet->getMissingTitles(), 'missing' );
- self::addValues( $result, $pageSet->getInterwikiTitlesAsResult() );
+ $result = $pageSet->getInvalidTitlesAndRevisions();
foreach ( $pageSet->getGoodTitles() as $title ) {
$r = array();
}
$dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
- $from = ( $params['from'] === null ? null : $this->titlePartToKey( $params['from'], NS_CATEGORY ) );
- $to = ( $params['to'] === null ? null : $this->titlePartToKey( $params['to'], NS_CATEGORY ) );
+ $from = ( $params['from'] === null
+ ? null
+ : $this->titlePartToKey( $params['from'], NS_CATEGORY ) );
+ $to = ( $params['to'] === null
+ ? null
+ : $this->titlePartToKey( $params['to'], NS_CATEGORY ) );
$this->addWhereRange( 'cat_title', $dir, $from, $to );
$min = $params['min'];
$this->titlePartToKey( $params['to'], $params['namespace'] ) );
$this->addWhereRange( $pfx . $fieldTitle, 'newer', $from, $to );
-
if ( isset( $params['prefix'] ) ) {
$this->addWhere( $pfx . $fieldTitle . $db->buildLike( $this->titlePartToKey(
$params['prefix'], $params['namespace'] ), $db->anyString() ) );
$this->addWhereFld( 'page_namespace', $params['namespace'] );
$dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
- $from = ( $params['from'] === null ? null : $this->titlePartToKey( $params['from'], $params['namespace'] ) );
- $to = ( $params['to'] === null ? null : $this->titlePartToKey( $params['to'], $params['namespace'] ) );
+ $from = ( $params['from'] === null
+ ? null
+ : $this->titlePartToKey( $params['from'], $params['namespace'] ) );
+ $to = ( $params['to'] === null
+ ? null
+ : $this->titlePartToKey( $params['to'], $params['namespace'] ) );
$this->addWhereRange( 'page_title', $dir, $from, $to );
if ( isset( $params['prefix'] ) ) {
} elseif ( $mode == 'all' ) {
$this->addWhereFld( 'ar_namespace', $params['namespace'] );
- $from = $params['from'] === null ? null : $this->titlePartToKey( $params['from'], $params['namespace'] );
- $to = $params['to'] === null ? null : $this->titlePartToKey( $params['to'], $params['namespace'] );
+ $from = $params['from'] === null
+ ? null
+ : $this->titlePartToKey( $params['from'], $params['namespace'] );
+ $to = $params['to'] === null
+ ? null
+ : $this->titlePartToKey( $params['to'], $params['namespace'] );
$this->addWhereRange( 'ar_title', $dir, $from, $to );
if ( isset( $params['prefix'] ) ) {
' (requires url and param ' . $modulePrefix . 'urlwidth)',
'mediatype' => ' mediatype - Adds the media type of the image',
'metadata' => ' metadata - Lists Exif metadata for the version of the image',
- 'commonmetadata' => ' commonmetadata - Lists file format generic metadata for the version of the image',
+ 'commonmetadata' => ' commonmetadata - Lists file format generic metadata ' .
+ 'for the version of the image',
'extmetadata' => ' extmetadata - Lists formatted metadata combined ' .
'from multiple sources. Results are HTML formatted.',
'archivename' => ' archivename - Adds the file name of the archive ' .
$ret['vcs-url'] = isset( $svnInfo['viewvc-url'] ) ? $svnInfo['viewvc-url'] : '';
}
}
+
if ( SpecialVersion::getExtLicenseFileName( $extensionPath ) ) {
$ret['license-name'] = isset( $ext['license-name'] ) ? $ext['license-name'] : '';
- $ret['license'] = SpecialPage::getTitleFor( 'Version', "License/{$ext['name']}" )->getLinkURL();
+ $ret['license'] = SpecialPage::getTitleFor(
+ 'Version',
+ "License/{$ext['name']}"
+ )->getLinkURL();
}
+
if ( SpecialVersion::getExtAuthorsFileName( $extensionPath ) ) {
- $ret['credits'] = SpecialPage::getTitleFor( 'Version', "Credits/{$ext['name']}" )->getLinkURL();
+ $ret['credits'] = SpecialPage::getTitleFor(
+ 'Version',
+ "Credits/{$ext['name']}"
+ )->getLinkURL();
}
}
$data[] = $ret;
$vals['commenthidden'] = '';
$anyHidden = true;
}
- if ( Revision::userCanBitfield( $row->rev_deleted, Revision::DELETED_COMMENT, $this->getUser() ) ) {
+
+ $userCanView = Revision::userCanBitfield(
+ $row->rev_deleted,
+ Revision::DELETED_COMMENT, $this->getUser()
+ );
+
+ if ( $userCanView ) {
if ( $this->fld_comment ) {
$vals['comment'] = $row->rev_comment;
}
--- /dev/null
+<?php
+/**
+ * Created on Jun 25, 2013
+ *
+ * Copyright © 2013 Brad Jorsch <bjorsch@wikimedia.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @since 1.23
+ */
+
+/**
+ * API interface to RevDel. The API equivalent of Special:RevisionDelete.
+ * Requires API write mode to be enabled.
+ *
+ * @ingroup API
+ */
+class ApiRevisionDelete extends ApiBase {
+
+ public function execute() {
+ $params = $this->extractRequestParams();
+ $user = $this->getUser();
+
+ if ( !$user->isAllowed( RevisionDeleter::getRestriction( $params['type'] ) ) ) {
+ $this->dieUsageMsg( 'badaccess-group0' );
+ }
+
+ if ( !$params['ids'] ) {
+ $this->dieUsage( "At least one value is required for 'ids'", 'badparams' );
+ }
+
+ $hide = $params['hide'] ?: array();
+ $show = $params['show'] ?: array();
+ if ( array_intersect( $hide, $show ) ) {
+ $this->dieUsage( "Mutually exclusive values for 'hide' and 'show'", 'badparams' );
+ } elseif ( !$hide && !$show ) {
+ $this->dieUsage( "At least one value is required for 'hide' or 'show'", 'badparams' );
+ }
+ $bits = array(
+ 'content' => RevisionDeleter::getRevdelConstant( $params['type'] ),
+ 'comment' => Revision::DELETED_COMMENT,
+ 'user' => Revision::DELETED_USER,
+ );
+ $bitfield = array();
+ foreach ( $bits as $key => $bit ) {
+ if ( in_array( $key, $hide ) ) {
+ $bitfield[$bit] = 1;
+ } elseif ( in_array( $key, $show ) ) {
+ $bitfield[$bit] = 0;
+ } else {
+ $bitfield[$bit] = -1;
+ }
+ }
+
+ if ( $params['suppress'] === 'yes' ) {
+ if ( !$user->isAllowed( 'suppressrevision' ) ) {
+ $this->dieUsageMsg( 'badaccess-group0' );
+ }
+ $bitfield[Revision::DELETED_RESTRICTED] = 1;
+ } elseif ( $params['suppress'] === 'no' ) {
+ $bitfield[Revision::DELETED_RESTRICTED] = 0;
+ } else {
+ $bitfield[Revision::DELETED_RESTRICTED] = -1;
+ }
+
+ $targetObj = null;
+ if ( $params['target'] ) {
+ $targetObj = Title::newFromText( $params['target'] );
+ }
+ $targetObj = RevisionDeleter::suggestTarget( $params['type'], $targetObj, $params['ids'] );
+ if ( $targetObj === null ) {
+ $this->dieUsage( 'A target title is required for this RevDel type', 'needtarget' );
+ }
+
+ $list = RevisionDeleter::createList(
+ $params['type'], $this->getContext(), $targetObj, $params['ids']
+ );
+ $status = $list->setVisibility(
+ array( 'value' => $bitfield, 'comment' => $params['reason'], 'perItemStatus' => true )
+ );
+
+ $result = $this->getResult();
+ $data = $this->extractStatusInfo( $status );
+ $data['target'] = $targetObj->getFullText();
+ $data['items'] = array();
+
+ foreach ( $status->itemStatuses as $id => $s ) {
+ $data['items'][$id] = $this->extractStatusInfo( $s );
+ $data['items'][$id]['id'] = $id;
+ }
+
+ $list->reloadFromMaster();
+ // @codingStandardsIgnoreStart Avoid function calls in a FOR loop test part
+ for ( $item = $list->reset(); $list->current(); $item = $list->next() ) {
+ $data['items'][$item->getId()] += $item->getApiData( $this->getResult() );
+ }
+ // @codingStandardsIgnoreEnd
+
+ $data['items'] = array_values( $data['items'] );
+ $result->setIndexedTagName( $data['items'], 'i' );
+ $result->addValue( null, $this->getModuleName(), $data );
+ }
+
+ private function extractStatusInfo( $status ) {
+ $ret = array(
+ 'status' => $status->isOK() ? 'Success' : 'Fail',
+ );
+ $errors = $this->formatStatusMessages( $status->getErrorsByType( 'error' ) );
+ if ( $errors ) {
+ $this->getResult()->setIndexedTagName( $errors, 'e' );
+ $ret['errors'] = $errors;
+ }
+ $warnings = $this->formatStatusMessages( $status->getErrorsByType( 'warning' ) );
+ if ( $warnings ) {
+ $this->getResult()->setIndexedTagName( $warnings, 'w' );
+ $ret['warnings'] = $warnings;
+ }
+ return $ret;
+ }
+
+ private function formatStatusMessages( $messages ) {
+ if ( !$messages ) {
+ return array();
+ }
+ $result = $this->getResult();
+ $ret = array();
+ foreach ( $messages as $m ) {
+ $message = array();
+ if ( $m['message'] instanceof Message ) {
+ $msg = $m['message'];
+ $message = array( 'message' => $msg->getKey() );
+ if ( $msg->getParams() ) {
+ $message['params'] = $msg->getParams();
+ $result->setIndexedTagName( $message['params'], 'p' );
+ }
+ } else {
+ $message = array( 'message' => $m['message'] );
+ $msg = wfMessage( $m['message'] );
+ if ( isset( $m['params'] ) ) {
+ $message['params'] = $m['params'];
+ $result->setIndexedTagName( $message['params'], 'p' );
+ $msg->params( $m['params'] );
+ }
+ }
+ $message['rendered'] = $msg->useDatabase( false )->inLanguage( 'en' )->plain();
+ $ret[] = $message;
+ }
+ return $ret;
+ }
+
+ public function mustBePosted() {
+ return true;
+ }
+
+ public function isWriteMode() {
+ return true;
+ }
+
+ public function getAllowedParams() {
+ return array(
+ 'type' => array(
+ ApiBase::PARAM_TYPE => RevisionDeleter::getTypes(),
+ ApiBase::PARAM_REQUIRED => true
+ ),
+ 'target' => null,
+ 'ids' => array(
+ ApiBase::PARAM_ISMULTI => true,
+ ApiBase::PARAM_REQUIRED => true
+ ),
+ 'hide' => array(
+ ApiBase::PARAM_TYPE => array( 'content', 'comment', 'user' ),
+ ApiBase::PARAM_ISMULTI => true,
+ ),
+ 'show' => array(
+ ApiBase::PARAM_TYPE => array( 'content', 'comment', 'user' ),
+ ApiBase::PARAM_ISMULTI => true,
+ ),
+ 'suppress' => array(
+ ApiBase::PARAM_TYPE => array( 'yes', 'no', 'nochange' ),
+ ApiBase::PARAM_DFLT => 'nochange',
+ ),
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
+ 'reason' => null,
+ );
+ }
+
+ public function getParamDescription() {
+ return array(
+ 'type' => 'Type of revision deletion being performed',
+ 'target' => 'Page title for the revision deletion, if required for the type',
+ 'ids' => 'Identifiers for the revisions to be deleted',
+ 'hide' => 'What to hide for each revision',
+ 'show' => 'What to unhide for each revision',
+ 'suppress' => 'Whether to suppress data from administrators as well as others',
+ 'token' => 'A delete token previously retrieved through action=tokens',
+ 'reason' => 'Reason for the deletion/undeletion',
+ );
+ }
+
+ public function getDescription() {
+ return 'Delete/undelete revisions';
+ }
+
+ public function getPossibleErrors() {
+ return array_merge( parent::getPossibleErrors(),
+ array(
+ 'needtarget' => 'A target title is required for this RevDel type',
+ 'badparams' => 'Bad value for some parameter',
+ )
+ );
+ }
+
+ public function needsToken() {
+ return true;
+ }
+
+ public function getTokenSalt() {
+ return '';
+ }
+
+ public function getExamples() {
+ return array(
+ 'api.php?action=revisiondelete&target=Main%20Page&type=revision&ids=12345&' .
+ 'hide=content&token=123ABC'
+ => 'Hide content for revision 12345 on the Main Page',
+ 'api.php?action=revisiondelete&type=logging&ids=67890&hide=content|comment|user&' .
+ 'reason=BLP%20violation&token=123ABC'
+ => 'Hide all data on log entry 67890 with the reason "BLP violation"',
+ );
+ }
+
+ public function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:Revisiondelete';
+ }
+}
* @ingroup API
*/
class ApiWatch extends ApiBase {
+ private $mPageSet = null;
public function execute() {
$user = $this->getUser();
if ( !$user->isLoggedIn() ) {
$this->dieUsage( 'You must be logged-in to have a watchlist', 'notloggedin' );
}
+
if ( !$user->isAllowed( 'editmywatchlist' ) ) {
$this->dieUsage( 'You don\'t have permission to edit your watchlist', 'permissiondenied' );
}
$params = $this->extractRequestParams();
- $title = Title::newFromText( $params['title'] );
+ $pageSet = $this->getPageSet();
+ // by default we use pageset to extract the page to work on.
+ // title is still supported for backward compatibility
+ if ( !isset( $params['title'] ) ) {
+ $pageSet->execute();
+ $res = $pageSet->getInvalidTitlesAndRevisions( array(
+ 'invalidTitles',
+ 'special',
+ 'missingIds',
+ 'missingRevIds',
+ 'interwikiTitles'
+ ) );
+
+ foreach ( $pageSet->getMissingTitles() as $title ) {
+ $r = $this->watchTitle( $title, $user, $params );
+ $r['missing'] = 1;
+ $res[] = $r;
+ }
+
+ foreach ( $pageSet->getGoodTitles() as $title ) {
+ $r = $this->watchTitle( $title, $user, $params );
+ $res[] = $r;
+ }
+ $this->getResult()->setIndexedTagName( $res, 'w' );
+ } else {
+ // dont allow use of old title parameter with new pageset parameters.
+ $extraParams = array_keys( array_filter( $pageSet->extractRequestParams(), function ( $x ) {
+ return $x !== null && $x !== false;
+ } ) );
+
+ if ( $extraParams ) {
+ $p = $this->getModulePrefix();
+ $this->dieUsage(
+ "The parameter {$p}title can not be used with " . implode( ", ", $extraParams ),
+ 'invalidparammix'
+ );
+ }
+
+ $title = Title::newFromText( $params['title'] );
+ if ( !$title || !$title->isWatchable() ) {
+ $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) );
+ }
+ $res = $this->watchTitle( $title, $user, $params, true );
+ }
+ $this->getResult()->addValue( null, $this->getModuleName(), $res );
+ }
- if ( !$title || !$title->isWatchable() ) {
- $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) );
+ private function watchTitle( Title $title, User $user, array $params,
+ $compatibilityMode = false
+ ) {
+ if ( !$title->isWatchable() ) {
+ return array( 'title' => $title->getPrefixedText(), 'watchable' => 0 );
}
$res = array( 'title' => $title->getPrefixedText() );
}
if ( $params['unwatch'] ) {
- $res['unwatched'] = '';
- $res['message'] = $this->msg( 'removedwatchtext', $title->getPrefixedText() )
- ->title( $title )->parseAsBlock();
$status = UnwatchAction::doUnwatch( $title, $user );
+ if ( $status->isOK() ) {
+ $res['unwatched'] = '';
+ $res['message'] = $this->msg( 'removedwatchtext', $title->getPrefixedText() )
+ ->title( $title )->parseAsBlock();
+ }
} else {
- $res['watched'] = '';
- $res['message'] = $this->msg( 'addedwatchtext', $title->getPrefixedText() )
- ->title( $title )->parseAsBlock();
$status = WatchAction::doWatch( $title, $user );
+ if ( $status->isOK() ) {
+ $res['watched'] = '';
+ $res['message'] = $this->msg( 'addedwatchtext', $title->getPrefixedText() )
+ ->title( $title )->parseAsBlock();
+ }
}
if ( !is_null( $oldLang ) ) {
}
if ( !$status->isOK() ) {
- $this->dieStatus( $status );
+ if ( $compatibilityMode ) {
+ $this->dieStatus( $status );
+ }
+ $res['error'] = $this->getErrorFromStatus( $status );
}
- $this->getResult()->addValue( null, $this->getModuleName(), $res );
+ return $res;
+ }
+
+
+ /**
+ * Get a cached instance of an ApiPageSet object
+ * @return ApiPageSet
+ */
+ private function getPageSet() {
+ if ( $this->mPageSet === null ) {
+ $this->mPageSet = new ApiPageSet( $this );
+ }
+ return $this->mPageSet;
}
public function mustBePosted() {
return 'watch';
}
- public function getAllowedParams() {
- return array(
+ public function getAllowedParams( $flags = 0 ) {
+ $result = array(
'title' => array(
ApiBase::PARAM_TYPE => 'string',
- ApiBase::PARAM_REQUIRED => true
+ ApiBase::PARAM_DEPRECATED => true
),
'unwatch' => false,
'uselang' => null,
ApiBase::PARAM_REQUIRED => true
),
);
+ if ( $flags ) {
+ $result += $this->getPageSet()->getFinalParams( $flags );
+ }
+ return $result;
}
public function getParamDescription() {
- return array(
- 'title' => 'The page to (un)watch',
+ $psModule = $this->getPageSet();
+ return $psModule->getParamDescription() + array(
+ 'title' => 'The page to (un)watch. use titles instead',
'unwatch' => 'If set the page will be unwatched rather than watched',
'uselang' => 'Language to show the message in',
'token' => 'A token previously acquired via prop=info',
}
public function getDescription() {
- return 'Add or remove a page from/to the current user\'s watchlist';
+ return 'Add or remove pages from/to the current user\'s watchlist';
}
public function getPossibleErrors() {
public function getExamples() {
return array(
- 'api.php?action=watch&title=Main_Page' => 'Watch the page "Main Page"',
- 'api.php?action=watch&title=Main_Page&unwatch=' => 'Unwatch the page "Main Page"',
+ 'api.php?action=watch&titles=Main_Page' => 'Watch the page "Main Page"',
+ 'api.php?action=watch&titles=Main_Page&unwatch=' => 'Unwatch the page "Main Page"',
);
}
return $text;
}
- wfDebug( __METHOD__ . "()\n", false );
+ wfDebug( __METHOD__ . "()\n", 'log' );
$now = wfTimestampNow();
if ( $this->useGzip() ) {
try {
$compiledRules = CLDRPluralRuleEvaluator::compile( $rules );
} catch ( CLDRPluralRuleError $e ) {
- wfDebugLog( 'l10n', $e->getMessage() . "\n" );
+ wfDebugLog( 'l10n', $e->getMessage() );
return array();
}
}
} catch ( RedisException $e ) {
$this->downServers[$server] = time() + self::SERVER_DOWN_TTL;
- wfDebugLog( 'redis', "Redis exception connecting to $server: " . $e->getMessage() . "\n" );
+ wfDebugLog( 'redis', "Redis exception connecting to $server: " . $e->getMessage() );
return false;
}
* @param RedisException $e
*/
public function handleException( $server, RedisConnRef $cref, RedisException $e ) {
- wfDebugLog( 'redis', "Redis exception on server $server: " . $e->getMessage() . "\n" );
+ wfDebugLog( 'redis', "Redis exception on server $server: " . $e->getMessage() );
foreach ( $this->connections[$server] as $key => $connection ) {
if ( $cref->isConnIdentical( $connection['conn'] ) ) {
$this->idlePoolSize -= $connection['free'] ? 1 : 0;
* @param array $join_conds
* @return string
*/
- protected function tableNamesWithUseIndexOrJOIN( $tables, $use_index = array(), $join_conds = array() ) {
+ protected function tableNamesWithUseIndexOrJOIN( $tables, $use_index = array(),
+ $join_conds = array()
+ ) {
$ret = array();
$retJOIN = array();
$use_index_safe = is_array( $use_index ) ? $use_index : array();
* can be locked. That means tables in an outer join cannot be FOR UPDATE locked. Trying to do
* so causes a DB error. This wrapper checks which tables can be locked and adjusts it accordingly.
*/
- function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__, $options = array(), $join_conds = array() ) {
+ function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__,
+ $options = array(), $join_conds = array()
+ ) {
if ( is_array( $options ) ) {
$forUpdateKey = array_search( 'FOR UPDATE', $options );
if ( $forUpdateKey !== false && $join_conds ) {
/**
* Return the next in a sequence, save the value for retrieval via insertId()
- *
+ *
* @param string $seqName
* @return int|null
*/
if ( isset( $options['FOR UPDATE'] ) ) {
$postLimitTail .= ' FOR UPDATE OF ' . implode( ', ', $options['FOR UPDATE'] );
- } else if ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
+ } elseif ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
$postLimitTail .= ' FOR UPDATE';
}
foreach ( $lags as $i => $lag ) {
if ( $i != 0 ) {
if ( $lag === false ) {
- wfDebugLog( 'replication', "Server #$i is not replicating\n" );
+ wfDebugLog( 'replication', "Server #$i is not replicating" );
unset( $loads[$i] );
} elseif ( isset( $this->mServers[$i]['max lag'] ) && $lag > $this->mServers[$i]['max lag'] ) {
- wfDebugLog( 'replication', "Server #$i is excessively lagged ($lag seconds)\n" );
+ wfDebugLog( 'replication', "Server #$i is excessively lagged ($lag seconds)" );
unset( $loads[$i] );
}
}
$i = $this->getRandomNonLagged( $currentLoads, $wiki );
if ( $i === false && count( $currentLoads ) != 0 ) {
# All slaves lagged. Switch to read-only mode
- wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode\n" );
+ wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode" );
$wgReadOnly = 'The database has been automatically locked ' .
'while the slave database servers catch up to the master';
$i = ArrayUtils::pickRandom( $currentLoads );
# pickRandom() returned false
# This is permanent and means the configuration or the load monitor
# wants us to return false.
- wfDebugLog( 'connect', __METHOD__ . ": pickRandom() returned false\n" );
+ wfDebugLog( 'connect', __METHOD__ . ": pickRandom() returned false" );
return false;
}
wfDebugLog( 'connect', __METHOD__ .
- ": Using reader #$i: {$this->mServers[$i]['host']}...\n" );
+ ": Using reader #$i: {$this->mServers[$i]['host']}..." );
$conn = $this->openConnection( $i, $wiki );
if ( !$conn ) {
- wfDebugLog( 'connect', __METHOD__ . ": Failed connecting to $i/$wiki\n" );
+ wfDebugLog( 'connect', __METHOD__ . ": Failed connecting to $i/$wiki" );
unset( $nonErrorLoads[$i] );
unset( $currentLoads[$i] );
continue;
# If all servers were down, quit now
if ( !count( $nonErrorLoads ) ) {
- wfDebugLog( 'connect', "All servers down\n" );
+ wfDebugLog( 'connect', "All servers down" );
}
if ( $i !== false ) {
* @since 1.19
* @param $msg string
* @param $callerOffset int
- * @param $level int A PHP error level. See sendWarning()
+ * @param $level int A PHP error level. See sendMessage()
* @param $log string: 'production' will always trigger a php error, 'auto'
* will trigger an error if $wgDevelopmentWarnings is true, and 'debug'
* will only write to the debug log(s).
$callerDescription = self::getCallerDescription( $callerOffset );
- self::sendWarning( $msg, $callerDescription, $level );
+ self::sendMessage( $msg, $callerDescription, 'warning', $level );
if ( self::$enabled ) {
self::$log[] = array(
if ( $sendToLog ) {
global $wgDevelopmentWarnings; // we could have a more specific $wgDeprecationWarnings setting.
- self::sendWarning(
+ self::sendMessage(
$msg,
$callerDescription,
+ 'deprecated',
$wgDevelopmentWarnings ? E_USER_DEPRECATED : false
);
}
}
/**
- * Send a warning to the debug log and optionally also trigger a PHP
+ * Send a message to the debug log and optionally also trigger a PHP
* error, depending on the $level argument.
*
* @param $msg string Message to send
* @param $caller array caller description get from getCallerDescription()
+ * @param $group string log group on which to send the message
* @param $level int|bool error level to use; set to false to not trigger an error
*/
- private static function sendWarning( $msg, $caller, $level ) {
+ private static function sendMessage( $msg, $caller, $group, $level ) {
$msg .= ' [Called from ' . $caller['func'] . ' in ' . $caller['file'] . ']';
if ( $level !== false ) {
trigger_error( $msg, $level );
}
- wfDebug( "$msg\n" );
+ wfDebugLog( $group, $msg, 'log' );
}
/**
return;
}
- wfDebugLog( 'squid', __METHOD__ . ': ' . implode( ' ', $urlArr ) . "\n" );
+ wfDebugLog( 'squid', __METHOD__ . ': ' . implode( ' ', $urlArr ) );
if ( $wgHTCPRouting ) {
self::HTCPPurge( $urlArr );
if ( !$conn ) {
$errstr = socket_strerror( socket_last_error() );
wfDebugLog( 'squid', __METHOD__ .
- ": Error opening UDP socket: $errstr\n" );
+ ": Error opening UDP socket: $errstr" );
wfProfileOut( __METHOD__ );
return;
$conf = self::getRuleForURL( $url, $wgHTCPRouting );
if ( !$conf ) {
wfDebugLog( 'squid', __METHOD__ .
- "No HTCP rule configured for URL {$url} , skipping\n" );
+ "No HTCP rule configured for URL {$url} , skipping" );
continue;
}
$htcpTransID, $htcpSpecifier, 2 );
wfDebugLog( 'squid', __METHOD__ .
- "Purging URL $url via HTCP\n" );
+ "Purging URL $url via HTCP" );
foreach ( $conf as $subconf ) {
socket_sendto( $conn, $htcpPacket, $htcpLen, 0,
$subconf['host'], $subconf['port'] );
$cacheID = ( $itemID === false ) ? "$cluster/$id" : "$cluster/$id/";
if ( isset( $externalBlobCache[$cacheID] ) ) {
wfDebugLog( 'ExternalStoreDB-cache',
- "ExternalStoreDB::fetchBlob cache hit on $cacheID\n" );
+ "ExternalStoreDB::fetchBlob cache hit on $cacheID" );
return $externalBlobCache[$cacheID];
}
wfDebugLog( 'ExternalStoreDB-cache',
- "ExternalStoreDB::fetchBlob cache miss on $cacheID\n" );
+ "ExternalStoreDB::fetchBlob cache miss on $cacheID" );
$dbr = $this->getSlave( $cluster );
$ret = $dbr->selectField( $this->getTable( $dbr ),
'blob_text', array( 'blob_id' => $id ), __METHOD__ );
if ( $ret === false ) {
wfDebugLog( 'ExternalStoreDB',
- "ExternalStoreDB::fetchBlob master fallback on $cacheID\n" );
+ "ExternalStoreDB::fetchBlob master fallback on $cacheID" );
// Try the master
$dbw = $this->getMaster( $cluster );
$ret = $dbw->selectField( $this->getTable( $dbw ),
'blob_text', array( 'blob_id' => $id ), __METHOD__ );
if ( $ret === false ) {
wfDebugLog( 'ExternalStoreDB',
- "ExternalStoreDB::fetchBlob master failed to find $cacheID\n" );
+ "ExternalStoreDB::fetchBlob master failed to find $cacheID" );
}
}
if ( $itemID !== false && $ret !== false ) {
if ( $ids ) {
wfDebugLog( __CLASS__, __METHOD__ .
" master fallback on '$cluster' for: " .
- implode( ',', array_keys( $ids ) ) . "\n" );
+ implode( ',', array_keys( $ids ) ) );
// Try the master
$dbw = $this->getMaster( $cluster );
$res = $dbw->select( $this->getTable( $dbr ),
array( 'blob_id' => array_keys( $ids ) ),
__METHOD__ );
if ( $res === false ) {
- wfDebugLog( __CLASS__, __METHOD__ . " master failed on '$cluster'\n" );
+ wfDebugLog( __CLASS__, __METHOD__ . " master failed on '$cluster'" );
} else {
$this->mergeBatchResult( $ret, $ids, $res );
}
if ( $ids ) {
wfDebugLog( __CLASS__, __METHOD__ .
" master on '$cluster' failed locating items: " .
- implode( ',', array_keys( $ids ) ) . "\n" );
+ implode( ',', array_keys( $ids ) ) );
}
return $ret;
}
// Run all requests for the first stage, then the next, and so on
- for ( $stage = 0; $stage < count( $httpReqsByStage ); ++$stage ) {
+ $reqCount = count( $httpReqsByStage );
+ for ( $stage = 0; $stage < $reqCount; ++$stage ) {
$httpReqs = $this->http->runMulti( $httpReqsByStage[$stage] );
foreach ( $httpReqs as $index => $httpReq ) {
// Run the callback for each request of this operation
if ( $this->isVForm() ) {
// mw-ui-block is necessary because the buttons aren't necessarily in an
// immediate child div of the vform.
- // TODO Let client specify if the primary submit button is progressive or destructive
- array_push( $attribs['class'], 'mw-ui-button', 'mw-ui-big', 'mw-ui-constructive', 'mw-ui-block' );
+ // @todo Let client specify if the primary submit button is progressive or destructive
+ array_push(
+ $attribs['class'],
+ 'mw-ui-button',
+ 'mw-ui-big',
+ 'mw-ui-constructive',
+ 'mw-ui-block'
+ );
}
$html .= Xml::submitButton( $this->getSubmitText(), $attribs ) . "\n";
* @since 1.17
*/
class MysqlUpdater extends DatabaseUpdater {
-
protected function getCoreUpdateList() {
return array(
array( 'disableContentHandlerUseDB' ),
// 1.23
array( 'addField', 'recentchanges', 'rc_source', 'patch-rc_source.sql' ),
- array( 'addIndex', 'logging', 'log_user_text_type_time', 'patch-logging_user_text_type_time_index.sql' ),
- array( 'addIndex', 'logging', 'log_user_text_time', 'patch-logging_user_text_time_index.sql' ),
+ array( 'addIndex', 'logging', 'log_user_text_type_time',
+ 'patch-logging_user_text_type_time_index.sql' ),
+ array( 'addIndex', 'logging', 'log_user_text_time', 'patch-logging_user_text_time_index.sql' ),
array( 'addField', 'page', 'page_links_updated', 'patch-page_links_updated.sql' ),
);
}
array( 'addPgIndex', 'job', 'job_cmd_token', '(job_cmd, job_token, job_random)' ),
array( 'addPgIndex', 'job', 'job_cmd_token_id', '(job_cmd, job_token, job_id)' ),
array( 'addPgIndex', 'filearchive', 'fa_sha1', '(fa_sha1)' ),
- array( 'addPgIndex', 'logging', 'logging_user_text_type_time', '(log_user_text, log_type, log_timestamp)' ),
+ array( 'addPgIndex', 'logging', 'logging_user_text_type_time',
+ '(log_user_text, log_type, log_timestamp)' ),
array( 'addPgIndex', 'logging', 'logging_user_text_time', '(log_user_text, log_timestamp)' ),
array( 'checkIndex', 'pagelink_unique', array(
// 1.23
array( 'addField', 'recentchanges', 'rc_source', 'patch-rc_source.sql' ),
- array( 'addIndex', 'logging', 'log_user_text_type_time', 'patch-logging_user_text_type_time_index.sql' ),
+ array( 'addIndex', 'logging', 'log_user_text_type_time',
+ 'patch-logging_user_text_type_time_index.sql' ),
array( 'addIndex', 'logging', 'log_user_text_time', 'patch-logging_user_text_time_index.sql' ),
array( 'addField', 'page', 'page_links_updated', 'patch-page_links_updated.sql' ),
);
}
if ( $action === true ) {
- wfDebugLog( $this->log, "$class::$fname: accepted: '$in' (type: $type)\n" );
+ wfDebugLog( $this->log, "$class::$fname: accepted: '$in' (type: $type)" );
} elseif ( $action === false ) {
- wfDebugLog( $this->log, "$class::$fname: rejected: '$in' (type: $type)\n" );
+ wfDebugLog( $this->log, "$class::$fname: rejected: '$in' (type: $type)" );
} elseif ( $action === null ) {
- wfDebugLog( $this->log, "$class::$fname: input was: '$in' (type: $type)\n" );
+ wfDebugLog( $this->log, "$class::$fname: input was: '$in' (type: $type)" );
} else {
- wfDebugLog( $this->log, "$class::$fname: $action (type: $type; content: '$in')\n" );
+ wfDebugLog( $this->log, "$class::$fname: $action (type: $type; content: '$in')" );
}
}
}
$class = ucfirst( __CLASS__ );
if ( $io ) {
- wfDebugLog( $this->log, "$class::$fname: begin processing: '{$this->basename}'\n" );
+ wfDebugLog( $this->log, "$class::$fname: begin processing: '{$this->basename}'" );
} else {
- wfDebugLog( $this->log, "$class::$fname: end processing: '{$this->basename}'\n" );
+ wfDebugLog( $this->log, "$class::$fname: end processing: '{$this->basename}'" );
}
}
}
* Log a fatal error
*/
protected function logError( $msg ) {
- wfDebugLog( 'redis', "Redis error: $msg\n" );
+ wfDebugLog( 'redis', "Redis error: $msg" );
}
/**
}
return "<li>$difflink $revlink $userlink $comment</li>";
}
+
+ public function getApiData( ApiResult $result ) {
+ $rev = $this->revision;
+ $user = $this->list->getUser();
+ $ret = array(
+ 'id' => $rev->getId(),
+ 'timestamp' => wfTimestamp( TS_ISO_8601, $rev->getTimestamp() ),
+ );
+ $ret += $rev->isDeleted( Revision::DELETED_USER ) ? array( 'userhidden' => '' ) : array();
+ $ret += $rev->isDeleted( Revision::DELETED_COMMENT ) ? array( 'commenthidden' => '' ) : array();
+ $ret += $rev->isDeleted( Revision::DELETED_TEXT ) ? array( 'texthidden' => '' ) : array();
+ if ( $rev->userCan( Revision::DELETED_USER, $user ) ) {
+ $ret += array(
+ 'userid' => $rev->getUser( Revision::FOR_THIS_USER ),
+ 'user' => $rev->getUserText( Revision::FOR_THIS_USER ),
+ );
+ }
+ if ( $rev->userCan( Revision::DELETED_COMMENT, $user ) ) {
+ $ret += array(
+ 'comment' => $rev->getComment( Revision::FOR_THIS_USER ),
+ );
+ }
+ return $ret;
+ }
}
/**
return '<li>' . $this->getLink() . ' ' . $this->getUserTools() . ' ' .
$data . ' ' . $this->getComment() . '</li>';
}
+
+ public function getApiData( ApiResult $result ) {
+ $file = $this->file;
+ $user = $this->list->getUser();
+ $ret = array(
+ 'title' => $this->list->title->getPrefixedText(),
+ 'archivename' => $file->getArchiveName(),
+ 'timestamp' => wfTimestamp( TS_ISO_8601, $file->getTimestamp() ),
+ 'width' => $file->getWidth(),
+ 'height' => $file->getHeight(),
+ 'size' => $file->getSize(),
+ );
+ $ret += $file->isDeleted( Revision::DELETED_USER ) ? array( 'userhidden' => '' ) : array();
+ $ret += $file->isDeleted( Revision::DELETED_COMMENT ) ? array( 'commenthidden' => '' ) : array();
+ $ret += $this->isDeleted() ? array( 'contenthidden' => '' ) : array();
+ if ( !$this->isDeleted() ) {
+ $ret += array(
+ 'url' => $file->getUrl(),
+ );
+ } elseif ( $this->canViewContent() ) {
+ $ret += array(
+ 'url' => SpecialPage::getTitleFor( 'Revisiondelete' )->getLinkURL(
+ array(
+ 'target' => $this->list->title->getPrefixedText(),
+ 'file' => $file->getArchiveName(),
+ 'token' => $user->getEditToken( $file->getArchiveName() )
+ ),
+ false, PROTO_RELATIVE
+ ),
+ );
+ }
+ if ( $file->userCan( Revision::DELETED_USER, $user ) ) {
+ $ret += array(
+ 'userid' => $file->user,
+ 'user' => $file->user_text,
+ );
+ }
+ if ( $file->userCan( Revision::DELETED_COMMENT, $user ) ) {
+ $ret += array(
+ 'comment' => $file->description,
+ );
+ }
+ return $ret;
+ }
}
/**
return "<li>$loglink $date $action $comment</li>";
}
+
+ public function getApiData( ApiResult $result ) {
+ $logEntry = DatabaseLogEntry::newFromRow( $this->row );
+ $user = $this->list->getUser();
+ $ret = array(
+ 'id' => $logEntry->getId(),
+ 'type' => $logEntry->getType(),
+ 'action' => $logEntry->getSubtype(),
+ );
+ $ret += $logEntry->isDeleted( LogPage::DELETED_USER ) ? array( 'userhidden' => '' ) : array();
+ $ret += $logEntry->isDeleted( LogPage::DELETED_COMMENT ) ? array( 'commenthidden' => '' ) : array();
+ $ret += $logEntry->isDeleted( LogPage::DELETED_ACTION ) ? array( 'actionhidden' => '' ) : array();
+
+ if ( LogEventsList::userCan( $this->row, LogPage::DELETED_ACTION, $user ) ) {
+ ApiQueryLogEvents::addLogParams(
+ $result,
+ $ret,
+ $logEntry->getParameters(),
+ $logEntry->getType(),
+ $logEntry->getSubtype(),
+ $logEntry->getTimestamp(),
+ $logEntry->isLegacy()
+ );
+ }
+ if ( LogEventsList::userCan( $this->row, LogPage::DELETED_USER, $user ) ) {
+ $ret += array(
+ 'userid' => $this->row->log_user,
+ 'user' => $this->row->log_user_text,
+ );
+ }
+ if ( LogEventsList::userCan( $this->row, LogPage::DELETED_COMMENT, $user ) ) {
+ $ret += array(
+ 'comment' => $this->row->log_comment,
+ );
+ }
+ return $ret;
+ }
}
* transactions are done here.
*
* @param array $params Associative array of parameters. Members are:
- * value: The integer value to set the visibility to
- * comment: The log comment.
+ * value: The integer value to set the visibility to
+ * comment: The log comment.
+ * perItemStatus: Set if you want per-item status reports
* @return Status
+ * @since 1.23 Added 'perItemStatus' param
*/
public function setVisibility( $params ) {
$bitPars = $params['value'];
$comment = $params['comment'];
+ $perItemStatus = isset( $params['perItemStatus'] ) ? $params['perItemStatus'] : false;
$this->res = false;
$dbw = wfGetDB( DB_MASTER );
$idsForLog = array();
$authorIds = $authorIPs = array();
+ if ( $perItemStatus ) {
+ $status->itemStatuses = array();
+ }
+
for ( $this->reset(); $this->current(); $this->next() ) {
$item = $this->current();
unset( $missing[$item->getId()] );
+ if ( $perItemStatus ) {
+ $itemStatus = Status::newGood();
+ $status->itemStatuses[$item->getId()] = $itemStatus;
+ } else {
+ $itemStatus = $status;
+ }
+
$oldBits = $item->getBits();
// Build the actual new rev_deleted bitfield
$newBits = RevisionDeleter::extractBitfield( $bitPars, $oldBits );
if ( $oldBits == $newBits ) {
- $status->warning( 'revdelete-no-change', $item->formatDate(), $item->formatTime() );
+ $itemStatus->warning( 'revdelete-no-change', $item->formatDate(), $item->formatTime() );
$status->failCount++;
continue;
} elseif ( $oldBits == 0 && $newBits != 0 ) {
if ( $item->isHideCurrentOp( $newBits ) ) {
// Cannot hide current version text
- $status->error( 'revdelete-hide-current', $item->formatDate(), $item->formatTime() );
+ $itemStatus->error( 'revdelete-hide-current', $item->formatDate(), $item->formatTime() );
$status->failCount++;
continue;
}
// Cannot access this revision
$msg = ( $opType == 'show' ) ?
'revdelete-show-no-access' : 'revdelete-modify-no-access';
- $status->error( $msg, $item->formatDate(), $item->formatTime() );
+ $itemStatus->error( $msg, $item->formatDate(), $item->formatTime() );
$status->failCount++;
continue;
}
// Cannot just "hide from Sysops" without hiding any fields
if ( $newBits == Revision::DELETED_RESTRICTED ) {
- $status->warning( 'revdelete-only-restricted', $item->formatDate(), $item->formatTime() );
+ $itemStatus->warning( 'revdelete-only-restricted', $item->formatDate(), $item->formatTime() );
$status->failCount++;
continue;
}
$authorIPs[] = $item->getAuthorName();
}
} else {
- $status->error( 'revdelete-concurrent-change', $item->formatDate(), $item->formatTime() );
+ $itemStatus->error( 'revdelete-concurrent-change', $item->formatDate(), $item->formatTime() );
$status->failCount++;
}
}
// Handle missing revisions
foreach ( $missing as $id => $unused ) {
- $status->error( 'revdelete-modify-missing', $id );
+ if ( $perItemStatus ) {
+ $status->itemStatuses[$id] = Status::newFatal( 'revdelete-modify-missing', $id );
+ } else {
+ $status->error( 'revdelete-modify-missing', $id );
+ }
$status->failCount++;
}
if ( $status->successCount == 0 ) {
- $status->ok = false;
$dbw->rollback( __METHOD__ );
return $status;
}
* @return boolean success
*/
abstract public function setBits( $newBits );
+
+ /**
+ * Get the return information about the revision for the API
+ * @since 1.23
+ * @param ApiResult $result API result object
+ * @return array Data for the API result
+ */
+ abstract public function getApiData( ApiResult $result );
}
global $wgSpecialPages;
global $wgDisableCounters, $wgDisableInternalSearch, $wgEmailAuthentication;
global $wgEnableEmail, $wgEnableJavaScriptTest;
- global $wgMiserMode;
if ( !is_object( self::$list ) ) {
wfProfileIn( __METHOD__ );
self::$list['JavaScriptTest'] = 'SpecialJavaScriptTest';
}
- if ( !$wgMiserMode ) {
- self::$list['Activeusers'] = 'SpecialActiveUsers';
- }
+ self::$list['Activeusers'] = 'SpecialActiveUsers';
// Add extension special pages
self::$list = array_merge( self::$list, $wgSpecialPages );
}
function getIndexField() {
- return 'rc_user_text';
+ return 'qcc_title';
}
function getQueryInfo() {
$dbr = $this->getDatabase();
- $conds = array( 'rc_user > 0' ); // Users - no anons
- $conds[] = 'rc_log_type IS NULL OR rc_log_type != ' . $dbr->addQuotes( 'newusers' );
- $conds[] = 'rc_timestamp >= ' . $dbr->addQuotes(
- $dbr->timestamp( wfTimestamp( TS_UNIX ) - $this->RCMaxAge * 24 * 3600 ) );
-
+ $conds = array(
+ 'qcc_type' => 'activeusers',
+ 'qcc_namespace' => NS_USER,
+ 'user_name = qcc_title',
+ 'rc_user_text = qcc_title'
+ );
if ( $this->requestedUser != '' ) {
- $conds[] = 'rc_user_text >= ' . $dbr->addQuotes( $this->requestedUser );
+ $conds[] = 'qcc_title >= ' . $dbr->addQuotes( $this->requestedUser );
}
-
if ( !$this->getUser()->isAllowed( 'hideuser' ) ) {
$conds[] = 'NOT EXISTS (' . $dbr->selectSQLText(
- 'ipblocks', '1', array( 'rc_user=ipb_user', 'ipb_deleted' => 1 )
+ 'ipblocks', '1', array( 'ipb_user=user_id', 'ipb_deleted' => 1 )
) . ')';
}
return array(
- 'tables' => array( 'recentchanges' ),
- 'fields' => array(
- 'user_name' => 'rc_user_text', // for Pager inheritance
- 'rc_user_text', // for Pager
- 'user_id' => 'MAX(rc_user)', // Postgres
- 'recentedits' => 'COUNT(*)'
- ),
- 'options' => array(
- 'GROUP BY' => array( 'rc_user_text' ),
- 'USE INDEX' => array( 'recentchanges' => 'rc_user_text' )
- ),
+ 'tables' => array( 'querycachetwo', 'user', 'recentchanges' ),
+ 'fields' => array( 'user_name', 'user_id', 'recentedits' => 'COUNT(*)', 'qcc_title' ),
+ 'options' => array( 'GROUP BY' => array( 'qcc_title' ) ),
'conds' => $conds
);
}
$out->wrapWikiMsg( "<div class='mw-activeusers-intro'>\n$1\n</div>",
array( 'activeusers-intro', $this->getLanguage()->formatNum( $wgActiveUserDays ) ) );
+ // Occasionally merge in new updates
+ $seconds = self::mergeActiveUsers( 600 );
+ // Mention the level of staleness
+ $out->addWikiMsg( 'cachedspecial-viewing-cached-ttl',
+ $this->getLanguage()->formatDuration( $seconds ) );
+
$up = new ActiveUsersPager( $this->getContext(), null, $par );
# getBody() first to check, if empty
protected function getGroupName() {
return 'users';
}
+
+ /**
+ * @param integer $period Seconds (do updates no more often than this)
+ * @return integer How many seconds old the cache is
+ */
+ public static function mergeActiveUsers( $period ) {
+ global $wgActiveUserDays;
+
+ $dbr = wfGetDB( DB_SLAVE );
+ $cTime = $dbr->selectField( 'querycache_info',
+ 'qci_timestamp',
+ array( 'qci_type' => 'activeusers' )
+ );
+ if ( !wfReadOnly() ) {
+ if ( !$cTime || ( time() - wfTimestamp( TS_UNIX, $cTime ) ) > $period ) {
+ $dbw = wfGetDB( DB_MASTER );
+ self::doQueryCacheUpdate( $dbw, 2 * $period );
+ }
+ }
+ return ( time() -
+ ( $cTime ? wfTimestamp( TS_UNIX, $cTime ) : $wgActiveUserDays * 86400 ) );
+ }
+
+ /**
+ * @param DatabaseBase $dbw Passed in from updateSpecialPages.php
+ * @return void
+ */
+ public static function cacheUpdate( DatabaseBase $dbw ) {
+ global $wgActiveUserDays;
+
+ self::doQueryCacheUpdate( $dbw, $wgActiveUserDays * 86400 );
+ }
+
+ /**
+ * Update the query cache as needed
+ *
+ * @param DatabaseBase $dbw
+ * @param integer $window Maximum time range of new data to scan (in seconds)
+ * @return bool Success
+ */
+ protected static function doQueryCacheUpdate( DatabaseBase $dbw, $window ) {
+ global $wgActiveUserDays;
+
+ $lockKey = wfWikiID() . '-activeusers';
+ if ( !$dbw->lock( $lockKey, __METHOD__, 1 ) ) {
+ return false; // exclusive update (avoids duplicate entries)
+ }
+
+ $now = time();
+ $cTime = $dbw->selectField( 'querycache_info',
+ 'qci_timestamp',
+ array( 'qci_type' => 'activeusers' )
+ );
+ $cTimeUnix = $cTime ? wfTimestamp( TS_UNIX, $cTime ) : 1;
+
+ // Pick the date range to fetch from. This is normally from the last
+ // update to till the present time, but has a limited window for sanity.
+ // If the window is limited, multiple runs are need to fully populate it.
+ $sTimestamp = max( $cTimeUnix, $now - $wgActiveUserDays * 86400 );
+ $eTimestamp = min( $sTimestamp + $window, $now );
+
+ // Get all the users active since the last update
+ $res = $dbw->select(
+ array( 'recentchanges' ),
+ array( 'rc_user_text', 'lastedittime' => 'MAX(rc_timestamp)' ),
+ array(
+ 'rc_user > 0', // actual accounts
+ 'rc_log_type IS NULL OR rc_log_type != ' . $dbw->addQuotes( 'newusers' ),
+ 'rc_timestamp >= ' . $dbw->addQuotes( $dbw->timestamp( $sTimestamp ) ),
+ 'rc_timestamp <= ' . $dbw->addQuotes( $dbw->timestamp( $eTimestamp ) )
+ ),
+ __METHOD__,
+ array(
+ 'GROUP BY' => array( 'rc_user_text' ),
+ 'ORDER BY' => 'NULL' // avoid filesort
+ )
+ );
+ $names = array();
+ foreach ( $res as $row ) {
+ $names[$row->rc_user_text] = $row->lastedittime;
+ }
+
+ // Rotate out users that have not edited in too long (according to old data set)
+ $dbw->delete( 'querycachetwo',
+ array(
+ 'qcc_type' => 'activeusers',
+ 'qcc_value < ' . $dbw->addQuotes( $now - $wgActiveUserDays * 86400 ) // TS_UNIX
+ ),
+ __METHOD__
+ );
+
+ // Find which of the recently active users are already accounted for
+ if ( count( $names ) ) {
+ $res = $dbw->select( 'querycachetwo',
+ array( 'user_name' => 'qcc_title' ),
+ array(
+ 'qcc_type' => 'activeusers',
+ 'qcc_namespace' => NS_USER,
+ 'qcc_title' => array_keys( $names ) ),
+ __METHOD__
+ );
+ foreach ( $res as $row ) {
+ unset( $names[$row->user_name] );
+ }
+ }
+
+ // Insert the users that need to be added to the list (which their last edit time
+ if ( count( $names ) ) {
+ $newRows = array();
+ foreach ( $names as $name => $lastEditTime ) {
+ $newRows[] = array(
+ 'qcc_type' => 'activeusers',
+ 'qcc_namespace' => NS_USER,
+ 'qcc_title' => $name,
+ 'qcc_value' => wfTimestamp( TS_UNIX, $lastEditTime ),
+ 'qcc_namespacetwo' => 0, // unused
+ 'qcc_titletwo' => '' // unused
+ );
+ }
+ foreach ( array_chunk( $newRows, 500 ) as $rowBatch ) {
+ $dbw->insert( 'querycachetwo', $rowBatch, __METHOD__ );
+ wfWaitForSlaves();
+ }
+ }
+
+ // Touch the data freshness timestamp
+ $dbw->replace( 'querycache_info',
+ array( 'qci_type' ),
+ array( 'qci_type' => 'activeusers',
+ 'qci_timestamp' => $dbw->timestamp( $eTimestamp ) ), // not always $now
+ __METHOD__
+ );
+
+ $dbw->unlock( $lockKey, __METHOD__ );
+
+ return true;
+ }
}
$title = $this->msg( 'passwordreset-emailtitle' );
- $this->result = $firstUser->sendMail( $title->escaped(), $this->email->text() );
+ $this->result = $firstUser->sendMail( $title->text(), $this->email->text() );
if ( isset( $data['Capture'] ) && $data['Capture'] ) {
// Save the user, will be used if an error occurs when sending the email
public function getFeedQuery() {
global $wgFeedLimit;
- $this->getOptions()->validateIntBounds( 'limit', 0, $wgFeedLimit );
$options = $this->getOptions()->getChangedValues();
// wfArrayToCgi() omits options set to null or false
}
unset( $value );
+ if ( isset( $options['limit'] ) && $options['limit'] > $wgFeedLimit ) {
+ $options['limit'] = $wgFeedLimit;
+ }
+
return wfArrayToCgi( $options );
}
if ( !is_null( $t ) ) {
global $wgGoToEdit;
wfRunHooks( 'SpecialSearchNogomatch', array( &$t ) );
- wfDebugLog( 'nogomatch', $t->getText(), false );
+ wfDebugLog( 'nogomatch', $t->getText(), 'private' );
# If the feature is enabled, go straight to the edit page
if ( $wgGoToEdit ) {
if ( !$status->isOk() ) {
return $status;
}
- wfDebugLog( 'fileconcatenate', "Combined $i chunks in $tAmount seconds.\n" );
+ wfDebugLog( 'fileconcatenate', "Combined $i chunks in $tAmount seconds." );
// File system path
$this->mTempPath = $tmpPath;
$this->mLocalFile = parent::stashFile( $this->user );
$tAmount = microtime( true ) - $tStart;
$this->mLocalFile->setLocalReference( $tmpFile ); // reuse (e.g. for getImageInfo())
- wfDebugLog( 'fileconcatenate', "Stashed combined file ($i chunks) in $tAmount seconds.\n" );
+ wfDebugLog( 'fileconcatenate', "Stashed combined file ($i chunks) in $tAmount seconds." );
return $status;
}
Калі Вы жадаеце загрузіць Ваш файл, вярніцеся назад і загрузіце гэты файл з новай назвай. [[File:$1|thumb|center|$1]]',
'file-exists-duplicate' => 'Гэты файл дублюе {{PLURAL:$1|1=наступны файл|наступныя файлы}}:',
'file-deleted-duplicate' => 'Падобны файл ([[:$1]]) ужо выдаляўся. Калі ласка, паглядзіце гісторыю выдаленьняў гэтага файла перад яго паўторнай загрузкай.',
+'file-deleted-duplicate-notitle' => 'Файл, ідэнтычны гэтаму файлу, раней ужо быў выдалены, а назва файла была забароненая.
+Вам трэба зьвярнуцца да некага з правамі прагляду зьвестак забароненых файлаў, каб прааналізаваць сытуацыю перад тым, як загружаць файл ізноў.',
'uploadwarning' => 'Папярэджаньне',
'uploadwarning-text' => 'Калі ласка, зьмяніце апісаньне файла ніжэй і паспрабуйце ізноў.',
'savefile' => 'Захаваць файл',
'listgrouprights-rights' => 'Правы',
'listgrouprights-helppage' => 'Help:Правы групаў удзельнікаў',
'listgrouprights-members' => '(сьпіс удзельнікаў групы)',
-'listgrouprights-addgroup' => 'можа дадаваць {{PLURAL:$2|1=групу|групы}}: $1',
-'listgrouprights-removegroup' => 'можа выдаляць {{PLURAL:$2|1=групу|групы}}: $1',
+'listgrouprights-addgroup' => 'можа дадаваць у {{PLURAL:$2|1=групу|групы}}: $1',
+'listgrouprights-removegroup' => 'можа выдаляць з {{PLURAL:$2|1=групы|групаў}}: $1',
'listgrouprights-addgroup-all' => 'можа дадаваць усе групы',
'listgrouprights-removegroup-all' => 'можа выдаляць усе групы',
'listgrouprights-addgroup-self' => 'Можа дадаць уласны рахунак да {{PLURAL:$2|1=групы|групаў}}: $1',
# Vector skin
'vector-action-addsection' => 'ТӀетоха хьедар',
'vector-action-delete' => 'ДӀаяккха',
-'vector-action-move' => 'Цlе хийца',
+'vector-action-move' => 'ЦӀе хийца',
'vector-action-protect' => 'Гlаролла дé',
'vector-action-undelete' => 'Меттахlоттадé',
'vector-action-unprotect' => 'ГӀароллех къаста',
ХӀу бахьна ду билгал дина дац.',
'no-null-revision' => '«$1» агӀона нисдар дан цаделира',
'badtitle' => 'Цамегаш йолу цlе',
-'badtitletext' => 'Дехарца йолу агlонан цlе нийса яц, йаьсса ю, хила мега нийса ца хlоттийна меттаюкъар йа юкъарвики цlе. Хила мега, цlарца цамагош йолу саберг.',
+'badtitletext' => 'Дехарца йолу агӀонан цӀе нийса яц, йаьсса ю, хила мега нийса ца хӀоттийна меттаюкъар йа юкъарвики цӀе. Хила мега, цӀарца цамагош йолу символаш.',
'perfcached' => 'Лахара хаам схьаэца кэша чура цундела тӀехьарлаьра хийцамаш гойтуш бац. Кэша чохь латтаё оцул $1 кӀезиг {{PLURAL:$1|1=дӀаяздар|дӀаяздарш}}.',
'perfcachedts' => 'Лахара хаам схьаэца кэша чура иза тӀаьхьара карла ялла $1. Кэша чохь латта до оцул $4 кӀезиг {{PLURAL:$4|1=дӀаяздар|дӀаяздарш}}.',
'querypage-no-updates' => 'ХӀинца хӀара агӀо карлаякхар дӀадайина ду.
# Diffs
'history-title' => '$1 — хийцаман истори',
'difference-title' => '$1 — Версешан башхалла',
-'lineno' => 'Могlа $1:',
+'lineno' => 'МогӀа $1:',
'compareselectedversions' => 'Хаьржина версеш муха ю хьажа',
'showhideselectedversions' => 'Гайта/къайлаяха хаьржина башхонаш',
'editundo' => 'цаоьшу',
'diff-empty' => '(башхалла яц)',
+'diff-multi-sameuser' => '(ца {{PLURAL:$1|гайтина юккъера цхьа верси|гайтина юккъера цхьа версеш}} оьцу декъашхочун)',
+'diff-multi-otherusers' => '(ца {{PLURAL:$1|гайтина юккъера верси|гайтина юккъера версеш}} {{PLURAL:$2|кхин цхьан декъашхочун|$2 декъашхойн}})',
# Search results
'searchresults' => 'Лахарна хилам',
'doubleredirects' => 'Шалха дIасахьажийнарш',
'doubleredirectstext' => 'ХӀокху агӀонехь ю дӀасахьажорашан тӀе хьажийна йолу дӀасахьажораш.
<del>ТӀехула сиз хаькхна </del>нисйина чарна.',
-'double-redirect-fixed-move' => 'Агlон [[$1]] цlе хийцна, хlинца иза дlахьажийна оцу [[$2]]',
+'double-redirect-fixed-move' => 'АгӀон [[$1]] цӀе хийцина, хӀинца иза дӀахьажийна оцу [[$2]]',
'brokenredirects' => 'ДIахаьдна долу дIасахьажораш',
'brokenredirectstext' => 'Лахара дӀасахьажийнарш ю йоцучу агӀонийн тӀе хьажийна:',
'newpages' => 'Керла агlонаш',
'newpages-username' => 'Декъашхо:',
'ancientpages' => 'Яззамаш оцу терахьца тӀаьххьара тадар дина долу',
-'move' => 'Цlе хийца',
-'movethispage' => 'Хlокху агlон цlе хийца',
+'move' => 'ЦӀе хийца',
+'movethispage' => 'ХӀокху агӀон цӀе хийца',
'unusedimagestext' => 'Дехар до, тидаме эца, кхин йолу дуьнана машан-меттигаш а лелош хила мега нийсса йогӀу хьажораг (URL) хӀокху хӀуман, хӀокху могӀаме йогӀуш ялахь яцахь а иза хила мега жигара лелош.',
'unusedcategoriestext' => 'ХӀокху категорешан чохь агӀонаш я кхин категореш яц.',
'notargettitle' => 'Ӏалашо билгал йина яц',
'allinnamespace' => 'Массо агlонаш оцу цlери анахь «$1»',
'allpagessubmit' => 'Кхочушдé',
'allpagesprefix' => 'Лаха агlонаш, дlайуьлалуш йолу:',
-'allpagesbadtitle' => 'Цамагош йолу агlон цlе. Коьрта могlан юкъах ю юкъарвики меттанашан юкъе тlечlагlйина йолу хьаьрк йа магийна доцу оцу коьрта моlанца сабол элп йа кхин.',
+'allpagesbadtitle' => 'Цамагош йолу агӀон цӀе. Коьрта могӀан юкъах ю юкъарвики меттанашан юкъе тӀечӀагӀйина йолу хьаьрк йа магийна доцу оцу коьрта моӀанца символаш йа кхин.',
'allpages-bad-ns' => '{{SITENAME}} кху чохь ана цlераш яц «$1».',
'allpages-hide-redirects' => 'Къайлаяха дӀасахьажийнарш',
'notanarticle' => 'Бац яззам',
'watchlist-details' => 'Хьан тергаме могlамца $1 {{PLURAL:$1|агlо|агlонаш|агlонаш}} ю, дийцаре агlонаша йоцуш.',
'wlheader-showupdated' => "Хийцам бина агӀонаш '''Ӏаьржа''' шрифтцан билгальяха ю.",
+'wlnote2' => 'Лахахьа гайтина {{PLURAL:$1|тӀеххьара сахьт}} чохь бина хийцамаш $2 $3.',
'wlshowlast' => 'Гайта тlаьххьара $1 сахьташ $2 денош $3',
'watchlist-options' => 'Тергаме могlаман гlирс нисбар',
'unprotectedarticle' => 'ГӀоролла дӀадаьстина «[[$1]]»',
'movedarticleprotection' => '«[[$2]]» агӀона тӀера гӀаролла «[[$1]]» агӀона тӀе даьккхина',
'protect-title' => 'Оцунна «$1» гӀоралла дар',
-'prot_1movedto2' => '«[[$1]]» цlе хийцина оцу «[[$2]]»',
+'prot_1movedto2' => '«[[$1]]» цӀе хийцина оцу «[[$2]]»',
'protect-legend' => 'Бакъде гӀоралла дар',
'protectcomment' => 'Бахьан:',
'protectexpiry' => 'Чекхйолу:',
'''ДӀАХЬЕДАР!'''
ЦӀе хийцар бахьнехь гӀаръялла агӀонашна дукха дагахь боцу хийцамаш хила тарло. Цундела цӀе хийцале шеш хила тарлучу тӀехьонашах кхета аьлла тешна хила.",
-'movepagetalktext' => "Тlе хlоьттина йолу дийцаре агlо ишта цlе хийцина хира ю, '''цхьа йолу ханчохь, маца:'''
+'movepagetalktext' => "ТӀе хӀоьттина йолу дийцаре агӀо ишта цӀе хийцина хира ю, '''цхьа йолу ханчохь, маца:'''
-*Йаьсса йоцу дийцаре агlо йолуш ю оцу цlарца йа
-*Ахьа къастаман харжам цабиняхь а къастам хlотточехь.
+*Йаьсса йоцу дийцаре агӀо йолуш ю оцу цӀарца йа
+*Ахьа къастаман харжам цабиняхь а къастам хӀотточехь.
-Ишта чу ханчохь, ахьа дехьа яккха йезар ю йа куьйга хlоттайар, нагахь иза хьашт йалахь.",
+Ишта чу ханчохь, ахьа дехьа яккха йезар ю йа куьйга хӀоттайар, нагахь иза хьашт йалахь.",
'movearticle' => 'Цle хийца хlокху агlон',
'moveuserpage-warning' => "'''Тергам бе.''' Хьо декъашхочун агӀона цӀе хийца гӀерта. Дехар до, тергам бе, декъашхочун агӀона цӀе бен хийца лур яц, декъашхочун дӀаяздаран цӀе хийца лур яц.",
'newtitle' => 'Керла цlе',
'move-watch' => 'Латайé хӀара агӀо тергаме могӀанан юкъахь',
-'movepagebtn' => 'Агlон цlе хийца',
-'pagemovedsub' => 'Агlон цlе хийцина',
-'movepage-moved' => "'''Агlон цlе «$1» хийцина хlокху «$2»'''",
+'movepagebtn' => 'АгӀон цӀе хийца',
+'pagemovedsub' => 'АгӀон цӀе хийцина',
+'movepage-moved' => "'''АгӀон цӀе «$1» хийцина хӀокху «$2»'''",
'movepage-moved-redirect' => 'Кхоьллина дӀасахьажориг.',
'movepage-moved-noredirect' => 'ДӀасхьажориг кхоллар дохина.',
-'articleexists' => 'Хlарасанна цlе йолу агlо йолуш ю йа ахьа гойтуш йолу цlе магош яц.
-Дехар до, харжа кхин цlе.',
-'movetalk' => 'Цуьнца йогlуш йолу дийцаре агlон цlе хийцар',
+'articleexists' => 'ХӀарасанна цӀе йолу агӀо йолуш ю йа ахьа гойтуш йолу цӀе магош яц.
+Дехар до, харжа кхин цӀе.',
+'movetalk' => 'Цуьнца йогӀуш йолу дийцаре агӀон цӀе хийцар',
'move-subpages' => 'ЦӀeрш хийцае бухара агӀонаши ($1 кхаччалц)',
'move-talk-subpages' => 'ЦӀе хийца бухара агӀонаши а агӀонашан дийцаре а ($1 кхаччалц)',
'movepage-page-exists' => 'Агӏо $1 йолуш ю цундела и ша юху дӏаязъян йиш яц.',
'tooltip-ca-protect' => 'Гlаролла дé хlокху агlон хийцам цабайта',
'tooltip-ca-unprotect' => 'Дlадаккха хlокху агlонна долу гаролла',
'tooltip-ca-delete' => 'ДӀаяккха хӀара агӀо',
-'tooltip-ca-move' => 'Агlон цlе хийца',
+'tooltip-ca-move' => 'АгӀон цӀе хийца',
'tooltip-ca-watch' => 'ТӀетоха хӀара агӀо сан тергаме могӀанан юкъа',
'tooltip-ca-unwatch' => 'ДӀаяккха хӀара агӀо хьай тергаме могӀанан юкъар',
'tooltip-search' => 'Лаха иза дош',
# Video information, used by Language::formatTimePeriod() to format lengths in the above messages
'seconds-abbrev' => '$1оцу',
+'minutes-abbrev' => '$1 мин',
+'hours-abbrev' => '$1 сахь.',
'hours' => '{{PLURAL:$1|1 сахьт}}',
'days' => '{{PLURAL:$1|$1 де}}',
'weeks' => '{{PLURAL:$1|$1 кӀира}}',
Шоьга кхача езаш яра [{{SERVER}}{{SCRIPTPATH}}/COPYING копи GNU General Public License] хӀокху программица, кхаьчна яцахь язъе Free Software Foundation, Inc., адрес тӀе: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA я [//www.gnu.org/licenses/old-licenses/gpl-2.0.html еша и онлайнехь].',
'version-software' => 'ДӀахӀоттийна программин латтор',
'version-software-version' => 'Верси',
+'version-entrypoints' => 'ЧугӀо адресин тӀадамаш',
# Special:Redirect
'redirect' => 'Декъашхочун файлан тӀера дӀасхьажор',
'logentry-newusers-newusers' => '{{GENDER:$2|ДӀавазвелла|ДӀаязелла}} керла декъашхо $1',
'logentry-newusers-create' => '{{GENDER:$2|ДӀавазвелла|ДӀаязелла}} керла декъашхо $1',
'logentry-newusers-autocreate' => 'Автоматически кхоьллина {{GENDER:$2|декъашхочун}} $1 дӀаяздар',
-'logentry-rights-rights' => '$1 {{GENDER:$2|хийцина}} хӀокхуна $3 бакъо $4 → $5',
+'logentry-rights-rights' => '$1 {{GENDER:$2|хийцина}} $3 бакъо $4 → $5',
'logentry-rights-rights-legacy' => '$1 {{GENDER:$2|хийцина}} хӏокхуна $3 бакъо',
'rightsnone' => '(яц)',
Bitte prüfe den Vergleich unten, um sicherzustellen, dass du dies tun möchtest, und speichere dann unten deine Änderungen, um die Bearbeitung rückgängig zu machen.',
'undo-failure' => 'Die Änderung konnte nicht rückgängig gemacht werden, da der betroffene Abschnitt zwischenzeitlich verändert wurde.',
'undo-norev' => 'Die Bearbeitung konnte nicht rückgängig gemacht werden, da sie nicht vorhanden ist oder gelöscht wurde.',
+'undo-nochange' => 'Anscheinend wurde diese Bearbeitung bereits rückgängig gemacht.',
'undo-summary' => 'Änderung $1 von [[Special:Contributions/$2|$2]] ([[User talk:$2|Diskussion]]) rückgängig gemacht.',
'undo-summary-username-hidden' => 'Änderung $1 eines versteckten Benutzers rückgängig gemacht.',
'last' => 'peyên',
'page_first' => 'verên',
'page_last' => 'peyên',
-'histlegend' => "Ferqê weçinayışi: Qutiya versiyonan mor ke u ''enter''i bıpıloxne ya zi makera cêrêne bıpıloxne.<br />
-Lecant: '''({{int:cur}})''' = ferqê versiyonê peyêni, '''({{int:last}})''' = ferqê versiyonê verêni, '''{{int:minoreditletter}}''' = vurnayışo werdi.",
+'histlegend' => "Ferqê weçinıtışi: Qutiya versiyonan seba têversanayış işaret ke û dest be ''enter''i ya zi gocega cêrêne ro ne.<br />
+Cedwel: <strong>({{int:ferq}})</strong> = ferqê verziyonê peyêni, <strong>({{int:peyên}})</strong> = ferqê versiyonê verêni, <strong>{{int:q}}</strong> = vurnayışo werdi.",
'history-fieldset-title' => 'Tarixi bıvêne',
'history-show-deleted' => 'Tenya esterıtey',
'histfirst' => 'Verênêr',
Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.',
'undo-failure' => 'The edit could not be undone due to conflicting intermediate edits.',
'undo-norev' => 'The edit could not be undone because it does not exist or was deleted.',
+'undo-nochange' => 'The edit appears to have already been undone.',
'undo-summary' => 'Undo revision $1 by [[Special:Contributions/$2|$2]] ([[User talk:$2|talk]])',
'undo-summary-username-hidden' => 'Undo revision $1 by a hidden user',
'retypenew' => 'Skriiw det paaswurd noch ans weder hen:',
'resetpass_submit' => 'Paaswurd saat an uunmelde',
'changepassword-success' => 'Din paaswurd as feranert wurden!',
+'changepassword-throttled' => 'Dü heest tufölsis fersoocht, di uuntumeldin.
+Wees so gud an teew $1, iar dü det noch ans ferschükst.',
'resetpass_forbidden' => 'Det paaswurd koon ei feranert wurd.',
'resetpass-no-info' => 'Dü skel di uunmelde, am üüb det sidj tutugripen.',
'resetpass-submit-loggedin' => 'Paaswurd feranre',
'changeemail-password' => 'Din {{SITENAME}} paaswurd:',
'changeemail-submit' => 'E-mail adres feranre',
'changeemail-cancel' => 'Ufbreeg',
+'changeemail-throttled' => 'Dü heest tufölsis fersoocht, di uuntumeldin.
+Wees so gud an teew $1, iar dü det noch ans ferschükst.',
# Special:ResetTokens
'resettokens' => 'Tokens turagsaat',
'invalid-content-data' => 'Diar stäänt wat uun, wat diar ei hen hiart',
'content-not-allowed-here' => '„$1“ mut ei skrewen wurd üüb sidj [[$2]]',
'editwarning-warning' => 'Wan dü detheer sidj slotst, kön feranrangen ferleesen gung.
-Üs uunmeldet brüker könst dü detheer wäärnang bi din iinstelangen oner „Bewerke“ wechknipse.',
+Üs uunmeldet brüker könst dü detheer wäärnang bi din iinstelangen oner „{{int:prefs-editing}}“ ufstel.',
'editpage-notsupportedcontentformat-title' => 'Detdiar formoot gongt ei.',
'editpage-notsupportedcontentformat-text' => 'Det formoot $1 gongt ei mä det model $2.',
'showhideselectedversions' => 'Werjuunen wise of fersteeg',
'editundo' => 'turagsaat',
'diff-empty' => '(nään ferskeel)',
+'diff-multi-sameuser' => '({{PLURAL:$1|Ian werjuun diartesken|$1 werjuunen diartesken}} faan disalew brüker {{PLURAL:$1|woort|wurd}} ei uunwiset)',
+'diff-multi-otherusers' => '({{PLURAL:$1|Ian werjuun diartesken|$1 werjuunen diartesken}} faan {{PLURAL:$2|ään öödern brüker|$2 ööder brükern}} {{PLURAL:$1|woort|wurd}} ei uunwiset)',
'diff-multi-manyusers' => '({{PLURAL:$1|Ian werjuun diartesken|$1 werjuunen diartesken}} faan muar üs $2 {{PLURAL:$2|brüker|brükern}} wurd ei uunwiset)',
'difference-missing-revision' => "{{PLURAL:$2|Ian werjuun|$2 werjuunen}} faan di ferskeel ($1) {{PLURAL:$2|as|san}} ei fünjen wurden.
'shown-title' => 'Wise $1 {{PLURAL:$1|resultaat|resultaaten}} per sidj',
'viewprevnext' => 'Wise ($1 {{int:pipe-separator}} $2) ($3)',
'searchmenu-exists' => "'''Deer as en sid nååmd \"[[:\$1]]\" önj jüdeer Wiki'''",
-'searchmenu-new' => "'''Maage det sidj „[[:$1]]“ uun detheer wiki.'''",
+'searchmenu-new' => '<strong>Maage det sidj „[[:$1]]“ uun detheer wiki!</strong> {{PLURAL:$2|0=|Luke uk bi det fünjen sidj.|Luke uk bi a fünjen sidjen.}}',
'searchprofile-articles' => 'Artiikler',
'searchprofile-project' => 'Halep- an Projektsidjen',
'searchprofile-images' => 'Multimedia',
'search-result-score' => 'Relewans: $1 %',
'search-redirect' => '(widjerfeerd faan „$1“)',
'search-section' => '(kirew $1)',
+'search-file-match' => '(fünjen tekst)',
'search-suggest' => 'Mendst dü „$1“?',
'search-interwiki-caption' => 'Saster-projekten',
'search-interwiki-default' => '$1 resultaaten:',
'watchmethod-list' => "Sidjen, diar dü uun't uug heest, am a leetst feranrangen beluke",
'watchlistcontains' => "Dü häälst $1 {{PLURAL:$1|sidj|sidjen}} uun't uug.",
'iteminvalidname' => 'Mä di iindrach „$1“ stemet wat ei, di nööm as ferkiard.',
+'wlnote2' => 'Diar stun a feranrangen faan a leetst {{PLURAL:$1|stünj|<strong>$1</strong> stünjen}}, üüb a stant faan $2, $3.',
'wlshowlast' => 'Wise a feranrangen faan leetst $1 stünjen, $2 daar of $3.',
'watchlist-options' => "Iinstelangen för't uunwisin",
'delete-edit-reasonlist' => "Grünjer för't striken bewerke",
'delete-toobig' => 'Detdiar sidj hää muar üs $1 {{PLURAL:$1|werjuun|werjuunen}} . Sok sidjen kön ei so gau stregen wurd, ööders san a servers plaat.',
'delete-warning-toobig' => "Detdiar sidj hää muar üs $1 {{PLURAL:$1|werjuun|werjuunen}} . Det striken koon komer maage bi't dootenbeenk.",
-'deleting-backlinks-warning' => "'''Paase üüb:''' Diar ferwise noch ööder sidjen üüb det sidj, diar dü strik wel.",
+'deleting-backlinks-warning' => "'''Paase üüb:''' Diar ferwise noch ööder sidjen üüb det sidj, diar dü strik wel. Of det sidj as noch huarööders iinbünjen.",
# Rollback
'rollback' => 'Feranrangen turagsaat',
'range_block_disabled' => 'Det mögelkhaid, hialer adresrümer tu sperin, as ei aktiif.',
'ipb_expiry_invalid' => 'Didiar tidjrüm gongt ei.',
'ipb_expiry_temp' => 'Ferbürgen brükernööm-speren skel permanent wees.',
-'ipb_hide_invalid' => 'Detdiar brükerkonto koon ei ferbürgen wurd, auer diar tuföl feranrangen uun a ferluup stun.',
+'ipb_hide_invalid' => 'Detdiar brükerkonto koon ei ferbürgen wurd, auer diar muar üs {{PLURAL:$1|ian feranrang|$1 feranrangen}} uun a ferluup stun.',
'ipb_already_blocked' => '„$1“ as al speret',
'ipb-needreblock' => '$1 as al speret. Wel dü a speriinstelangen feranre?',
'ipb-otherblocks-header' => 'Ööder {{PLURAL:$1|sper|speren}}',
'allmessages-prefix' => 'Filter mä prefix:',
'allmessages-language' => 'Spriak:',
'allmessages-filter-submit' => 'Widjer',
+'allmessages-filter-translate' => 'Auersaat',
# Thumbnails
'thumbnail-more' => 'Fergratre',
'importuploaderrortemp' => "Bi't huuchschüüren faan det importdatei as wat skiaf gingen. Diar as nian tidjwiis fertiaknis.",
'import-parse-failure' => "Bi't importiarin faan det XML-datei as wat skiaf gingen.",
'import-noarticle' => 'Diar as nian sidj tu importiarin bestemet wurden.',
-'import-nonewrevisions' => 'Aal jodiar werjuunen san al ans importiaret wurden.',
+'import-nonewrevisions' => 'Diar wurd nian werjuunen importiaret, auer jo eder al diar wiar of auersprüngen wurden san.',
'xml-error-string' => '$1 uun rä $2, türn $3 (byte $4): $5',
'import-upload' => 'XML-datein importiare',
'import-token-mismatch' => 'Session dooten san wech. Ferschük det noch ans weder.',
'import-error-special' => 'Det sidj „$1“ as ei importiaret wurden, auer hat tu en nöömrüm hiart, huar nian sidjen mögelk san.',
'import-error-invalid' => 'Det sidj „$1“ as ei importiaret wurden, auer di nööm ei stemet.',
'import-error-unserialize' => 'Det werjuun $2 faan det sidj „$1“ küd ei deserialisiaret wurd. Det werjuun woort mä det münster $3 brükt, an det as mä $4 serialisiaret.',
+'import-error-bad-location' => 'Det feranrang $2 mä model $3 koon ei üs "$1" üüb detheer wiki seekert wurd, auer detdiar model heer ei brükt wurd koon.',
'import-options-wrong' => 'Ferkiard {{PLURAL:$2|iinstelang|iinstelangen}}: <nowiki>$1</nowiki>',
'import-rootpage-invalid' => 'Didiar sidjennööm as ferkiard.',
'import-rootpage-nosubpage' => 'Uun di nöömrüm „$1“ jaft at nian onersidjen.',
'expand_templates_generate_rawhtml' => 'Rä HTML uunwise',
'expand_templates_preview' => 'Föörskau',
+# Unknown messages
+'createaccount-hook-aborted' => '$1',
+'uploadinvalidxml' => "Det XML uun det huuchschüürd datei küd ei ''parset'' wurd.",
);
'edit-gone-missing' => 'Бетті жаңарту мүмкін емес.
Мүмкін, бұл бет жойылған.',
'edit-conflict' => 'Өңдемелер қақтығысы.',
-'postedit-confirmation' => 'Сіздің өңдемеңіз сақталды.',
+'postedit-confirmation' => 'Өңдемеңіз сақталды.',
'edit-already-exists' => 'Жаңа бет жасау мүмкін емес.
Ол әлдеқашан бар.',
'defaultmessagetext' => 'Әдепкі мәтіні',
'undelete_short' => 'Traer atrás $1 {{PLURAL:$1|trocamientos|trocamientos}}',
'viewdeleted_short' => 'Ver {{PLURAL:$1|un trocamiento efassado|$1 trocamientos efassados}}',
'protect' => 'Guadrar',
-'protect_change' => 'trocar el guardadijo',
+'protect_change' => 'trocar',
'protectthispage' => 'Guardar esta hoja',
'unprotect' => 'Trocar guardadijo',
'unprotectthispage' => 'Trocar el guardadijo desta hoja',
'recentchangeslinked-feed' => 'Trocamientos conectados',
'recentchangeslinked-toolbox' => 'Trocamientos atados',
'recentchangeslinked-title' => 'Los trocamientos relacionados con "$1"',
-'recentchangeslinked-summary' => "Esto es la lista de los trocamientos dalcavo de las hojas que relatan a una hoja particòlar (o de los miembros de la kategoría particòlar).
-Las hojas en tu [[Special:Watchlist|lista de akavidamiento]] son '''reforçadas'''.",
+'recentchangeslinked-summary' => "Esto es una lista de trocamientos dalcavo en las hojas atadas de una hoja partikolara (u en los miembros de una kategoría partikolara).
+Las hojas en tu [[Special:Watchlist|lista de acavidamiento]] son '''reforçadas'''.",
'recentchangeslinked-page' => 'Nombre de la hoja',
'recentchangeslinked-to' => 'Amostra los trocamientos freskos en lugar de la hoja indicada',
'tooltip-ca-protect' => 'Guardar esta hoja',
'tooltip-ca-delete' => 'Efassar esta hoja',
'tooltip-ca-move' => 'Taxirea (renombra) esta hoja',
-'tooltip-ca-watch' => 'Ajustar esta hoja a tu lista de akavidamientos',
+'tooltip-ca-watch' => 'Ajusta esta hoja a tu lista de acavidamientos',
'tooltip-ca-unwatch' => 'Quita esta hoja de tu lista de escojidos',
'tooltip-search' => 'Buxca en {{SITENAME}}',
'tooltip-search-go' => 'Ir a la hoja con este nombre egzakto, si egziste.',
'tooltip-n-randompage' => 'Carga una hoja por azardo',
'tooltip-n-help' => 'Ambézate y topa ayudo',
'tooltip-t-whatlinkshere' => 'Una lista de todas las hojas del viki que tienen atamientos a esta hoja',
-'tooltip-t-recentchangeslinked' => 'Los trocamientos dalcavo de las hojas atadas a la ésta',
+'tooltip-t-recentchangeslinked' => 'Los trocamientos dalcavo en las hojas atadas a la ésta',
'tooltip-feed-rss' => 'Sindicación RSS de esta hoja',
-'tooltip-feed-atom' => "Fuente de Atom d'esta hoja",
+'tooltip-feed-atom' => 'Canal Atomo parâ esta hoja',
'tooltip-t-contributions' => 'Ver la lista de ajustamientos de este usuario',
'tooltip-t-emailuser' => 'A este usuario, mándale una letra electrόnica (ímey)',
'tooltip-t-upload' => 'Suve dosyas',
'tooltip-watch' => 'Ajusta esta hoja a tu lista de escojidas',
'tooltip-rollback' => '«Hazer aboltar» haze aboltar todos los trocamientos del usador dalcavo, sólo en klikando una vez.',
'tooltip-undo' => '«Des-hazer» abolta este trocamiento y lo avre en el modo de previsteo. Permete ajustar una razón en el somario.',
-'tooltip-summary' => 'Entrar un somaryo kurto',
+'tooltip-summary' => 'Entrar un somario curto',
# Attribution
'anonymous' => '{{PLURAL:$1|Uzuario anonimo|Uzuarios anonimos}} de {{SITENAME}}',
'invalid-content-data' => 'അസാധുവായ ഉള്ളടക്ക ഡേറ്റ',
'content-not-allowed-here' => '"$1" ഉള്ളടക്കം [[$2]] താളിൽ അനുവദിക്കുന്നില്ല',
'editwarning-warning' => 'ഈ താളിൽ നിന്നും പോകുന്നത് താങ്കൾ വരുത്തിയ മാറ്റങ്ങൾ നഷ്ടപ്പെടാൻ ഇടയാക്കും.
-താങ്കൾ ലോഗിൻ ചെയ്തിട്ടുണ്ടെങ്കിൽ, താങ്കളുടെ ക്രമീകരണങ്ങളിൽ "തിരുത്തൽ" എന്ന ഭാഗത്ത് ചെന്ന് ഈ അറിയിപ്പ് പ്രദർശിപ്പിക്കുന്നത് ഒഴിവാക്കാവുന്നതാണ്.',
+താങ്കൾ ലോഗിൻ ചെയ്തിട്ടുണ്ടെങ്കിൽ, താങ്കളുടെ ക്രമീകരണങ്ങളിൽ "{{int:prefs-editing}}" എന്ന ഭാഗത്ത് ചെന്ന് ഈ അറിയിപ്പ് പ്രദർശിപ്പിക്കുന്നത് ഒഴിവാക്കാവുന്നതാണ്.',
'editpage-notsupportedcontentformat-title' => 'ഉള്ളടക്ക ഫോർമാറ്റ് പിന്തുണയ്ക്കുന്നില്ല',
'editpage-notsupportedcontentformat-text' => 'ഉള്ളടക്കത്തിന്റെ ഫോർമാറ്റ് ആയ $1 ഉള്ളടക്ക രീതിയായ $2 പിന്തുണയ്ക്കുന്നില്ല.',
'showhideselectedversions' => 'തിരഞ്ഞെടുത്ത മാറ്റങ്ങൾ പ്രദർശിപ്പിക്കുക/മറയ്ക്കുക',
'editundo' => 'മാറ്റം തിരസ്ക്കരിക്കുക',
'diff-empty' => '(വ്യത്യാസം ഇല്ല)',
+'diff-multi-sameuser' => '(ഒരേ ഉപയോക്താവ് ചെയ്ത {{PLURAL:$1|ഇടയ്ക്കുള്ള ഒരു നാൾപ്പതിപ്പ്|ഇടയ്ക്കുള്ള $1 നാൾപ്പതിപ്പുകൾ}} പ്രദർശിപ്പിക്കുന്നില്ല)',
+'diff-multi-otherusers' => '({{PLURAL:$2|മറ്റൊരു ഉപയോക്താവ്|$2 ഉപയോക്താക്കൾ}} ചെയ്ത {{PLURAL:$1|ഇടയ്ക്കുള്ള ഒരു നാൾപ്പതിപ്പ്|ഇടയ്ക്കുള്ള $1 നാൾപ്പതിപ്പുകൾ}} പ്രദർശിപ്പിക്കുന്നില്ല)',
'diff-multi-manyusers' => '(ഇടയ്ക്ക് {{PLURAL:$2|ഒന്നിലധികം|$2 എണ്ണത്തിലധികം}} ഉപയോക്താക്കൾ ചെയ്തിട്ടുള്ള {{PLURAL:$1|ഒരു പതിപ്പ്|$1 പതിപ്പുകൾ}} പ്രദർശിപ്പിക്കുന്നില്ല.)',
'difference-missing-revision' => 'ഈ വ്യത്യാസത്തിൽ ($1) {{PLURAL:$2|ഒരു നാൾപ്പതിപ്പ്|$2 നാൾപ്പതിപ്പുകൾ}} കാണാനായില്ല.
'shown-title' => '{{PLURAL:$1|ഒരു ഫലം|$1 ഫലങ്ങൾ}} വീതം താളിൽ കാണിക്കുക',
'viewprevnext' => '$1 {{int:pipe-separator}} $2 എണ്ണം കാണുക ($3)',
'searchmenu-exists' => "'''\"[[:\$1]]\" എന്ന തലക്കെട്ടിൽ ഒരു താൾ ഈ വിക്കിയിൽ നിലവിലുണ്ട്'''",
-'searchmenu-new' => "'''ഈ വിക്കിയിൽ \"[[:\$1]]\" താൾ നിർമ്മിക്കുക!'''",
+'searchmenu-new' => '<strong>ഈ വിക്കിയിൽ "[[:$1]]" എന്ന താൾ സൃഷ്ടിക്കുക!</strong> {{PLURAL:$2|0=|ഒപ്പം താങ്കളുടെ തിരയലിനു ലഭിച്ച ഫലമായ ഈ താൾ കാണുക.|ഒപ്പം താങ്കളുടെ തിരയലിനു ലഭിച്ച ഫലങ്ങൾ കാണുക.}}',
'searchprofile-articles' => 'ലേഖനങ്ങളിൽ',
'searchprofile-project' => 'സഹായം, പദ്ധതി താളുകളിൽ',
'searchprofile-images' => 'പ്രമാണങ്ങളിൽ',
'delete-edit-reasonlist' => 'മായ്ക്കലിന്റെ കാരണം തിരുത്തുക',
'delete-toobig' => 'ഈ താളിനു വളരെ വിപുലമായ തിരുത്തൽ ചരിത്രമുണ്ട്. $1 മേൽ {{PLURAL:$1|പതിപ്പുണ്ട്|പതിപ്പുകളുണ്ട്}}. ഇത്തരം താളുകൾ മായ്ക്കുന്നതു {{SITENAME}} സംരംഭത്തിന്റെ നിലനില്പ്പിനെ തന്നെ ബാധിക്കുമെന്നതിനാൽ ഈ താൾ മായ്ക്കുന്നതിനുള്ള അവകാശം പരിമിതപ്പെടുത്തിയിരിക്കുന്നു.',
'delete-warning-toobig' => 'ഈ താളിനു വളരെ വിപുലമായ തിരുത്തൽ ചരിത്രമുണ്ട്. അതായത്, ഇതിനു് $1 മേൽ {{PLURAL:$1|പതിപ്പുണ്ട്|പതിപ്പുകളുണ്ട്}}. ഇത്തരം താളുകൾ മായ്ക്കുന്നതു {{SITENAME}} സംരംഭത്തിന്റെ ഡാറ്റാബേസ് ഓപ്പറേഷനെ ബാധിച്ചേക്കാം. അതിനാൽ വളരെ ശ്രദ്ധാപൂർവ്വം തുടർനടപടികളിലേക്കു നീങ്ങുക.',
-'deleting-backlinks-warning' => "'''മുന്നറിയിപ്പ്:''' മറ്റു താളുകളിൽ നിന്നും താളിലേയ്ക്കുള്ള കണ്ണികൾ താങ്കൾ മായ്ക്കാൻ പോവുകയാണ്.",
+'deleting-backlinks-warning' => "'''à´®àµ\81à´¨àµ\8dനറിയിപàµ\8dà´ªàµ\8d:''' മറàµ\8dà´±àµ\81 താളàµ\81à´\95ളിൽ നിനàµ\8dà´¨àµ\81à´\82 താളിലàµ\87à´¯àµ\8dà´\95àµ\8dà´\95àµ\81à´³àµ\8dà´³ à´\95à´£àµ\8dണിà´\95ൾ à´\85à´²àµ\8dà´²àµ\86à´\99àµ\8dà´\95ിൽ à´\89ൾപàµ\8dà´ªàµ\86à´\9fàµ\81à´¤àµ\8dതിയിà´\9fàµ\8dà´\9fàµ\81à´³àµ\8dà´³ താളàµ\81à´\95ൾ താà´\99àµ\8dà´\95ൾ മായàµ\8dà´\95àµ\8dà´\95ാൻ à´ªàµ\8bà´µàµ\81à´\95യാണàµ\8d.",
# Rollback
'rollback' => 'തിരുത്തലുകൾ റോൾബാക്ക് ചെയ്യുക',
തത്കാലത്തേയ്ക്കു വേണ്ടിയിരുന്ന ഒരു ഫോൾഡർ ലഭ്യമല്ല.',
'import-parse-failure' => 'എക്സ്.എം.എൽ. ഇറക്കുമതി പാഴ്സ് പരാജയം',
'import-noarticle' => 'ഇറക്കുമതി ചെയ്യാൻ താൾ ഇല്ല!',
-'import-nonewrevisions' => 'à´\8eà´²àµ\8dലാ പതിപàµ\8dà´ªàµ\81à´\95à´³àµ\81à´\82 à´®àµ\81à´®àµ\8dà´ªàµ\87 à´\87à´±à´\95àµ\8dà´\95àµ\81മതി à´\9aàµ\86à´¯àµ\8dതിà´\9fàµ\8dà´\9fàµ\81à´³àµ\8dളതാണàµ\8dâ\80\8c.',
+'import-nonewrevisions' => 'à´\92à´°àµ\81 നാൾപàµ\8dപതിപàµ\8dà´ªàµ\81à´\82 à´\87à´±à´\95àµ\8dà´\95àµ\81മതി à´\9aàµ\86à´¯àµ\8dതിà´\9fàµ\8dà´\9fà´¿à´²àµ\8dà´² (à´\8eà´²àµ\8dലാà´\82 നിലവിൽ à´\89à´£àµ\8dà´\9fàµ\8d, à´\85à´²àµ\8dà´²àµ\86à´\99àµ\8dà´\95ിൽ പിഴവàµ\81à´\95à´³àµ\81à´³àµ\8dളതിനാൽ à´\92ഴിവാà´\95àµ\8dà´\95à´¿).',
'xml-error-string' => '$2 വരിയിൽ $1, നിര $3 (ബൈറ്റ് $4): $5',
'import-upload' => 'എക്സ്.എം.എൽ. ഡേറ്റ അപ്ലോഡ് ചെയ്യുക',
'import-token-mismatch' => 'സെഷൻ ഡാറ്റ നഷ്ടപ്പെട്ടതിനാൽ ദയവായി വീണ്ടും ശ്രമിക്കൂക',
'expand_templates_generate_rawhtml' => 'അസംസ്കൃത എച്ച്.റ്റി.എം.എൽ. പ്രദർശിപ്പിക്കുക',
'expand_templates_preview' => 'എങ്ങനെയുണ്ടെന്നു കാണുക',
+# Unknown messages
+'uploadinvalidxml' => 'അപ്ലോഡ് ചെയ്ത പ്രമാണത്തിലെ എക്സ്.എം.എൽ. പാഴ്സ് ചെയ്യാൻ കഴിയില്ല.',
);
{{Identical|Undo}}',
'undo-norev' => 'Message appears if an attempt to revert an edit by clicking the "undo" link on the page history fails.
+{{Identical|Undo}}',
+'undo-nochange' => 'Message appears if an attempt to revert an edit by clicking the "undo" link results in an edit making no change to the current version of the page.
+
{{Identical|Undo}}',
'undo-summary' => 'Edit summary for an undo action. Parameters:
* $1 - revision ID
'youhavenewmessagesfromusers' => '$1 {{PLURAL:$3|huk ruraqmanta|$3 ruraqkunamanta}} qhawanayki kachkan ($2).',
'youhavenewmessagesmanyusers' => '$1 achka ruraqkunamanta qhawanayki kachkan ($2).',
'newmessageslinkplural' => '{{PLURAL:$1|Musuq willaymi|Musuq willaykunam}}',
-'newmessagesdifflinkplural' => 'qayna {{PLURAL:$1|hukchasqapi|hukchasqakunapi}} wakin kaynin',
+'newmessagesdifflinkplural' => 'ñaqha {{PLURAL:$1|hukchasqa|hukchasqakuna}}',
'youhavenewmessagesmulti' => 'Musuq willaykunam qhawanayki kachkan $1-pi',
'editsection' => "llamk'apuy",
'editold' => "llamk'apuy",
'invalidtitle-knownnamespace' => '"$2" sutisuyu, "$3" qillqasqayuq mana allin kaq qillqa suti',
'invalidtitle-unknownnamespace' => 'Mana riqsisqa $1 kaq sutisuyu yupay, "$2" qillqasqayuq mana allin kaq qillqa suti',
'exception-nologin' => 'Manam yaykurqankichu',
-'exception-nologin-text' => 'Kay wikipiqa icha kay ruranataqa rakiqunaykiwan yaykuspalla ruraytam atinki.',
+'exception-nologin-text' => "Ama hina kaspa [[Special:Userlogin|yaykuy]] kay p'anqaman rinata icha kayta ruranata atinaykipaq.",
# Virus scanner
'virus-badscanner' => "Manam allintachu churapusqa: mana riqsisqa añaw maskaq: ''$1''",
'gotaccount' => "Rakiqunaykiñachu kachkan? '''$1'''.",
'gotaccountlink' => 'Rakiqunaykita willaway',
'userlogin-resetlink' => 'Yaykuna willayniykikunatari qunqarqankichu?',
-'userlogin-resetpassword-link' => 'Yaykuna rimaykita kutichiy',
+'userlogin-resetpassword-link' => 'Yaykuna rimaykita qunqarqankichu?',
'helplogin-url' => 'Help:Yaykuy',
'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Yaykunapaq yanapa]]',
'userlogin-createanother' => 'Huk rakiqunata kamariy',
'passwordtooshort' => 'Yaykuna rimayqa {{PLURAL:$1|1 icha aswan sanampayuq|$1 icha aswan sanampayuq}} kananmi.',
'password-name-match' => 'Yaykuna rimaykiqa ruraqpa sutiykiman mana kaqlla kananmi.',
'password-login-forbidden' => 'Kay ruraqpa sutinmanqa yaykuna rimanmanpas ama nisqam.',
-'mailmypassword' => 'Musuq yaykuna rimata e-chaskiwan kachamuway',
+'mailmypassword' => 'Yaykuna rimata kutichiy',
'passwordremindertitle' => "{{SITENAME}}paq musuq mit'alla yaykuna rima",
'passwordremindertext' => 'Pipas (qamchiki, $1 IP huchhayuq tiyaymanta) mañakuwarqan {{SITENAME}}paq musuq yaykuna rimatam e-chaski imamaytaykiman kachayta ($4).
"$2" sutiyuq ruraqpa mit\'alla yaykuna rimanqa kamarisqañam, "$3" sutichasqam kachkan. Munarqaspaykiqa, kunan yaykuspa musuq yaykuna rimaykitam akllay.
'throttled-mailpassword' => "Huk yaykuna rima kutichinapaq yuyachina qayna {{PLURAL:$1|huk ura|$1 ura}} mit'api kachamusqañam. {{PLURAL:$1|Huk ura|$1 ura}} mit'apiqa hukllam yaykuna rima yuyachina kachasqa kachun millay rurayta hark'anapaq.",
'mailerror' => 'E-chaskita kachaspa pantasqa: $1',
'acct_creation_throttle_hit' => "Qampa IP huchhaykiyuq kachkaq ruraqkunaqa kay wikita watukuspa ñaqha 24 urapi {{PLURAL:$1|rakiqunaykim|$1 rakiqunaykim}} kamarirqanña. Manam atinkichikchu astawan kichayta huklla p'unchawpi chay IP huchhallayuq kaspa.",
-'emailauthenticated' => "E-chaski imamaytaykiqa $2 p'unchawpi, $3 pachapi chiqapchasqañam.",
-'emailnotauthenticated' => 'E-chaski imamaytaykitaqa manaraqmi takyachirqunkichu. Mana takyachirquptiykiqa, kay qatiq rurachinakunataqa manam atinkichu.',
+'emailauthenticated' => "E-chaski imamaytaykiqa $2 p'unchawpi, $3 pachapi takyachisqañam.",
+'emailnotauthenticated' => 'E-chaski imamaytaykitaqa manaraqmi takyachirqunkichu. Manaraq takyachirquptiykiqa, kay ruranakunapaq manam ima e-chaskipas kachasqa kanchu.',
'noemailprefs' => "E-chaski imamaytaykita willaway kay rurachinakunata llamk'achinapaq.",
'emailconfirmlink' => 'E-chaski imamaytaykita takyachiy',
'invalidemailaddress' => "E-chaski imamaytaykiqa manam allinchu, manachá allinta qillqasqa. Ama hina kaspa, musuq allin sananchayuq imamaytaykita qillqamuy icha k'itichata ch'usaqchay.",
'edit-already-exists' => "Manam atinichu musuq p'anqata kamariyta.
Kachkañam.",
'defaultmessagetext' => 'Ñawpaq qillqa',
-'content-failed-to-parse' => "Manam atinichu $2 samiqta $1 kikinchapaq t'ikrayta: $3",
+'content-failed-to-parse' => 'Manam atinichu $2 samiqta $1 kikinchapaq kuskiyta: $3',
'invalid-content-data' => 'Samiqmanta willaykunaqa manam allinchu',
'content-not-allowed-here' => '"$1" nisqa samiqqa [[$2]] sutiyuq p\'anqapi manam saqillasqachu',
'editwarning-warning' => "Kay p'anqata saqispaykiqa lliw rurarqusqayki hukchasqakunatachá chinkachiykiman.
{{SITENAME}}pi huk kamachiqkunaqa p'anqap pakasqa samiqninta qhawaspa qullusqa kaymanta kutichiyta atinkuraqmi kay kaqlla uyapuratam llamk'achispa, kay wikip kamariqninkuna mana huk saywachanakunata tiyachiptinqa.",
'revdelete-confirm' => 'Ama hina kaspa, takyachiy munayniykita, qatiqninkunata riqsiyniykita, [[{{MediaWiki:Policy-url}}|kawpaykama]] rurayniykitapas.',
'revdelete-suppress-text' => "Pakay ruranata '''kaylla kaptin''' llamk'achiy:
+* K'amiqchá willakuna
* Runamanta mana allin willakuna
-*: ''wasi tiyay imamaytakuna, karu rimay huchhakuna, allin kawsay tarikuq wasimanta huchhakuna, chay hinakunapas.''",
+*: ''wasi tiyay imamaytakuna, karu rimay huchhakuna, mamallaqta sut'inchay huchhakuna, chay hinakunapas.''",
'revdelete-legend' => 'Rikunapaq saywachanakunata tiyachiy',
-'revdelete-hide-text' => 'Qhawana qillqata pakay',
+'revdelete-hide-text' => 'Musuqmanta qhawana qillqa',
'revdelete-hide-image' => 'Willañiqip samiqninta pakay',
'revdelete-hide-name' => 'Rurayta paqtaytapas pakay',
-'revdelete-hide-comment' => "Llamk'apuymanta willapuyta pakay",
-'revdelete-hide-user' => 'Ruraqpa sutinta/IP huchhanta pakay',
+'revdelete-hide-comment' => "Llamk'apuymanta willapuy",
+'revdelete-hide-user' => 'Ruraqpa sutin/IP huchhan',
'revdelete-hide-restricted' => "Kamachiqkunamanta willakunata huk ruraqkunamanta hina hark'ay",
'revdelete-radio-same' => '(ama hukchaychu)',
-'revdelete-radio-set' => 'Arí',
-'revdelete-radio-unset' => 'Ama kachunchu',
+'revdelete-radio-set' => 'Pakasqa',
+'revdelete-radio-unset' => 'Rikunalla',
'revdelete-suppress' => 'Kamachiqkunamantapas willakunata huk ruraqkunamanta hina raqpay',
'revdelete-unsuppress' => "Qullusqamanta paqarisqa llamk'apusqakunapaq saywachanakunata raqpay",
'revdelete-log' => 'Kayrayku:',
'shown-title' => "Huk p'anqapi $1 {{PLURAL:$1|taripasqata|taripasqakunata}} rikuchiy",
'viewprevnext' => 'Qhaway ($1 {{int:pipe-separator}} $2) ($3).',
'searchmenu-exists' => "'''Kay wikipiqa «[[$1]]» sutiyuq p'anqam kachkan'''",
-'searchmenu-new' => "'''Kay wikipi \"[[:\$1]]\" sutiyuq p'anqata kamariy!'''",
+'searchmenu-new' => '<strong>Kay wikipi "[[:$1]]" sutiyuq p\'anqata kamariy!</strong> {{PLURAL:$2|0=|Maskayniykiwan tarisqa p\'anqatapas qhaway.|Maskaywan taripasqakunatapas qhaway.}}\'',
'searchprofile-articles' => "Samiq p'anqakuna",
'searchprofile-project' => "Yanapanapaq ruraykamaypaqpas p'anqakuna",
'searchprofile-images' => 'Multimidya',
'recentchanges-label-minor' => "Kayqa aslla llamk'apuymi",
'recentchanges-label-bot' => "Kayqa rurana antachap llamk'apusqanmi",
'recentchanges-label-unpatrolled' => "Kay llamk'apusqaqa manaraqmi patrullasqachu",
-'recentchanges-legend-newpage' => "$1 - musuq p'anqa",
+'recentchanges-legend-newpage' => "([[Special:NewPages|musuq p'anqakunatapas]] qhaway)",
'rcnotefrom' => "Kay qatiqpiqa '''$2'''-mantapacha ('''$1'''-kama) hukchasqakunatam rikunki.",
'rclistfrom' => '$1-manta musuq hukchasqakunata rikuchiy',
'rcshowhideminor' => "$1 uchuylla llamk'apusqakunata",
Kay p\'anqata mana musuqmanta watukamuptiykiqa, manam huk ruraykunamanta willasqaykichu. Tukuy watiqasqayki p\'anqakunapaq musyachina sananchakunatapas kutichiytam atinkiman.
- Tukuy sunquwan, {{SITENAME}}pa e-chaski musyachina llikan
+Tukuy sunquwan, {{SITENAME}}pa e-chaski musyachina llikan
--
E-chaski willaykuy allinkachinakunata hukchanaykipaqqa kay p\'anqatam qhaway:
'dellogpage' => 'Qullusqakuna',
'dellogpagetext' => 'Kay qatiqpiqa lliwmanta aswan ñaqha qullusqakunatam rikunki. Rikuchisqa pachankunaqa sirwiqpa pachanpim.',
'deletionlog' => 'qullusqakuna',
-'reverted' => 'Ñawpaq hukchasqata kutichiy',
+'reverted' => 'Ñawpaq hukchasqaman kutichisqa',
'deletecomment' => 'Kayrayku:',
'deleteotherreason' => 'Huk rayku:',
'deletereasonotherlist' => 'Huk rayku',
'range_block_disabled' => "Kamachiqpa patayayku hark'ay hayñinman ama nisqam.",
'ipb_expiry_invalid' => 'Puchukana pachaqa manam allinchu.',
'ipb_expiry_temp' => "Pakasqa ruraqpa sutin hark'aykunaqa tiyaqllam kachun.",
-'ipb_hide_invalid' => "Manam atinichu kay rakiqunata ñit'ipayta; nisyu llamk'apusqayuqñachá.",
+'ipb_hide_invalid' => "Manam atinichu kay rakiqunata ñit'ipayta; nisyu {{PLURAL:$1|llamk'apusqayuqñachá}}.",
'ipb_already_blocked' => '"$1" sutiyuqqa hark\'asqañam kachkan.',
'ipb-needreblock' => "$1 sutiyuqqa hark'asqañam. Allinchanakunata hukchayta munankichu?",
'ipb-otherblocks-header' => "Huk {{PLURAL:$1|hark'ay|hark'aykuna}}",
'importuploaderrorsize' => 'Manam atinichu hawamanta chaskina willañiqita churkuyta, saqillasqamanta aswan hatun kaptinmi.',
'importuploaderrorpartial' => 'Manam atinichu hawamanta chaskina willañiqita churkuyta, rakillam churkusqa.',
'importuploaderrortemp' => "Manam atinichu hawamanta chaskina willañiqita churkuyta, mit'alla willañiqi churana mana kaptinmi.",
-'import-parse-failure' => "Manam atinichu XML qillqata t'ikraspa hawamanta chaskiyta",
+'import-parse-failure' => 'Manam atinichu XML qillqata kuskispa hawamanta chaskiyta',
'import-noarticle' => "Manam hawamanta chaskina p'anqachu!",
'import-nonewrevisions' => 'Tukuy musuqchasqakunaqa ñawpaqtañam hawamanta chaskisqa.',
'xml-error-string' => "$1, $2 siq'ipi, $3 tunupi (byte $4): $5",
'svg-long-desc' => 'SVG willañiqi, rimasqakama $1 × $2 iñuyuq, willañiqip chhikan kaynin: $3',
'svg-long-desc-animated' => 'Kuyuchisqa SVG willañiqi, rimasqakama $1 × $2 iñuyuq, willañiqip chhikan kaynin: $3',
'svg-long-error' => 'Mana allin SVG willañiqi: $1',
-'show-big-image' => 'Qallariy huyaku',
+'show-big-image' => 'Qallariy willañiqi',
'show-big-image-preview' => 'Kay ñawpaq qhawariypa chhikan kaynin: $1.',
'show-big-image-other' => 'Huk {{PLURAL:$2|huyaku|huyakukuna}}: $1.',
'show-big-image-size' => '$1 × $2 iñu',
'expand_templates_remove_comments' => 'Willapusqakunata qichuy',
'expand_templates_preview' => 'Ñawpaqta qhawallay',
+# Unknown messages
+'uploadinvalidxml' => 'Manam atinichu churkusqa willañiqipi XML-ta kuskiyta.',
);
'summary' => 'Rezumat:',
'subject' => 'Subiect / titlu:',
'minoredit' => 'Aceasta este o modificare minoră',
-'watchthis' => 'Monitorizează această pagină',
+'watchthis' => 'Urmărește această pagină',
'savearticle' => 'Salvare pagină',
'preview' => 'Previzualizare',
'showpreview' => 'Previzualizare',
'invalid-content-data' => 'Date de conținut invalide',
'content-not-allowed-here' => 'Conținutul de tip „$1” nu este permis pe pagina [[$2]]',
'editwarning-warning' => 'Părăsind această pagină, există riscul pierderii modificărilor efectuate.
-Dacă sunteți autentificat, puteți dezactiva această avertizare în secțiunea „Modificare” a preferințelor dumneavoastră.',
+Dacă sunteți autentificat, puteți dezactiva această avertizare în secțiunea „{{int:prefs-editing}}” a preferințelor dumneavoastră.',
'editpage-notsupportedcontentformat-title' => 'Formatul conținutului nu este acceptat',
'editpage-notsupportedcontentformat-text' => 'Formatul de conținut $1 nu este acceptat de modelul de conținut $2.',
'showhideselectedversions' => 'Șterge/recuperează versiunile marcate',
'editundo' => 'anulare',
'diff-empty' => '(Nicio diferență)',
+'diff-multi-sameuser' => '(Nu {{PLURAL:$1|s-a afișat o versiune intermediară efectuată|s-au afișat $1 versiuni intermediare efectuate|s-au afișat $1 de versiuni intermediare efectuate}} de același utilizator)',
+'diff-multi-otherusers' => '(Nu {{PLURAL:$1|s-a afișat o versiune intermediară efectuată|s-au afișat $1 versiuni intermediare efectuate|s-au afișat $1 de versiuni intermediare efectuate}} de {{PLURAL:$2|un alt utilizator|alți $2 utilizatori|alți $2 de utilizatori}})',
'diff-multi-manyusers' => '({{PLURAL:$1|O versiune intermediară efectuată de|$1 (de) versiuni intermediare efectuate de peste}} $2 {{PLURAL:$2|utilizator|utilizatori}} {{PLURAL:$1|neafișată|neafișate}})',
'difference-missing-revision' => '{{PLURAL:$2|O versiune a|$2 versiuni ale|$2 de versiuni ale}} acestei diferențe ($1) nu {{PLURAL:$2|a fost găsită|au fost găsite}}.
'upload-http-error' => 'ఒక HTTP పొరపాటు జరిగింది: $1',
# File backend
+'backend-fail-stream' => '"$1" ఫైలును స్ట్రీమింగు చెయ్యలేకపోయాం.',
+'backend-fail-backup' => '"$1" ఫైలును బ్యాకప్పు చెయ్యలేకపోయాం.',
'backend-fail-notexists' => '$1 ఫైలు అసలు లేనేలేదు.',
+'backend-fail-hashes' => 'పోలిక కోసం ఫైలు హాష్లను పొందలేకపోయాం.',
+'backend-fail-notsame' => 'సరిగ్గా సరిపోయే ఫైలు కాదు గానీ, ఒక ఫైలు ఈసరికే "$1" వద్ద ఉంది.',
+'backend-fail-invalidpath' => '"$1" సరైన స్టోరేజి పాత్ కాదు',
'backend-fail-delete' => '$1 ఫైలును తొలగించలేకున్నాం.',
+'backend-fail-describe' => '"$1" ఫైలు మెటాడేటాను మార్చలేకపోయాం.',
'backend-fail-alreadyexists' => '$1 అనే దస్త్రం ఇప్పటికే ఉంది.',
+'backend-fail-store' => '"$1" ఫైలును "$2" వద్ద భద్రపరచలేకపోయాం.',
+'backend-fail-copy' => '"$1" నుండి "$2" కి ఫైలును కాపీ చెయ్యలేకపోయాం.',
+'backend-fail-move' => '"$1" నుండి "$2" కి ఫైలును తరలించలేకపోయాం.',
'backend-fail-opentemp' => 'తాత్కాలిక దస్త్రాన్ని తెరవలేకపోతున్నాం.',
+'backend-fail-writetemp' => 'తాత్కాలిక ఫైలులో రాయలేకపోయాం.',
'backend-fail-closetemp' => 'తాత్కాలిక దస్త్రాన్ని మూసివేయలేకపోయాం.',
'backend-fail-read' => '$1 దస్త్రము చదువలేకపోతిమి.',
'backend-fail-create' => '$1 ఫైలులో రాయలేకున్నాం.',
+'backend-fail-maxsize' => '"$1" ఫైలు {{PLURAL:$2|ఒక బైట్|$2 బైట్ల}} కంటే పెద్దది కావడం చేత దాన్ని రాయలేకపోయాం.',
+'backend-fail-readonly' => 'స్టోరేజి బ్యాక్ఎండ్ "$1" ప్రస్తుతం రీడ్-ఓన్లీ స్థితిలో ఉంది. దానికి కారణం: "<em>$2</em>"',
+'backend-fail-connect' => 'స్టోరేజీ బ్యాక్ఎండ్ "$1" కి కనెక్టు కాలేక పోయాం.',
+'backend-fail-internal' => 'స్టోరేజీ బ్యాక్ఎండ్ "$1" లో ఏదో తెలియని లోపం దొర్లింది.',
+'backend-fail-contenttype' => '"$1" లో దాచాల్సిన ఫైలు యొక్క కంటెంటు రకమేంటో నిర్ధారించలేకపోయాం.',
+'backend-fail-batchsize' => 'స్టోరేజీ బ్యాక్ఎండ్ కు $1 ఫైలు {{PLURAL:$1|ఆపరేషన్|ఆపరేషన్ల}} తో కూడిన బ్యాచ్ ఒకటి ఇవ్వబడింది; పరిమితి: $2 {{PLURAL:$2|ఆపరేషన్|ఆపరేషన్లు}}.',
# ZipDirectoryReader
'zip-file-open-error' => 'ఈ ఫైలును ZIP పరీక్ష కోసం తెరవబోతే, ఏదో తెలియని లోపం ఎదురైంది.',
'invalid-content-data' => 'Dữ liệu nội dung không hợp lệ',
'content-not-allowed-here' => 'Không cho phép đưa nội dung “$1” vào trang [[$2]]',
'editwarning-warning' => 'Rời khỏi trang này sẽ khiến bạn mất các sửa đổi đã thực hiện.
-Nếu đã đăng nhập, bạn có thể tắt cảnh báo này tại mục “Sửa đổi” trong tùy chọn cá nhân.',
+Nếu đã đăng nhập, bạn có thể tắt cảnh báo này tại mục “{{int:prefs-editing}}” trong tùy chọn cá nhân.',
'editpage-notsupportedcontentformat-title' => 'Không hỗ trợ định dạng nội dung',
'editpage-notsupportedcontentformat-text' => 'Mô hình nội dung $2 không hỗ trợ định dạng nội dung $1.',
--- /dev/null
+<?php
+/**
+ * Benchmark script for parse operations
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @author Tim Starling <tstarling@wikimedia.org>
+ * @ingroup Benchmark
+ */
+
+require __DIR__ . '/../Maintenance.php';
+
+/**
+ * Maintenance script to benchmark how long it takes to parse a given title at an optionally
+ * specified timestamp
+ *
+ * @since 1.23
+ */
+class BenchmarkParse extends Maintenance {
+ /** @var string MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS) */
+ private $templateTimestamp = null;
+
+ /** @var array Cache that maps a Title DB key to revision ID for the requested timestamp */
+ private $idCache = array();
+
+ function __construct() {
+ parent::__construct();
+ $this->addDescription( 'Benchmark parse operation' );
+ $this->addArg( 'title', 'The name of the page to parse' );
+ $this->addOption( 'cold', 'Don\'t repeat the parse operation to warm the cache' );
+ $this->addOption( 'page-time',
+ 'Use the version of the page which was current at the given time',
+ false, true );
+ $this->addOption( 'tpl-time',
+ 'Use templates which were current at the given time (except that moves and ' .
+ 'deletes are not handled properly)',
+ false, true );
+ }
+
+ function execute() {
+ if ( $this->hasOption( 'tpl-time' ) ) {
+ $this->templateTimestamp = wfTimestamp( TS_MW, strtotime( $this->getOption( 'tpl-time' ) ) );
+ Hooks::register( 'BeforeParserFetchTemplateAndtitle', array( $this, 'onFetchTemplate' ) );
+ }
+
+ $title = Title::newFromText( $this->getArg() );
+ if ( !$title ) {
+ $this->error( "Invalid title" );
+ exit( 1 );
+ }
+
+ if ( $this->hasOption( 'page-time' ) ) {
+ $pageTimestamp = wfTimestamp( TS_MW, strtotime( $this->getOption( 'page-time' ) ) );
+ $id = $this->getRevIdForTime( $title, $pageTimestamp );
+ if ( !$id ) {
+ $this->error( "The page did not exist at that time" );
+ exit( 1 );
+ }
+
+ $revision = Revision::newFromId( $id );
+ } else {
+ $revision = Revision::newFromTitle( $title );
+ }
+
+ if ( !$revision ) {
+ $this->error( "Unable to load revision, incorrect title?" );
+ exit( 1 );
+ }
+
+ if ( !$this->hasOption( 'cold' ) ) {
+ $this->runParser( $revision );
+ }
+
+ $startUsage = getrusage();
+ $startTime = microtime( true );
+ $this->runParser( $revision );
+ $endUsage = getrusage();
+ $endTime = microtime( true );
+
+ printf( "CPU time = %.3f s, wall clock time = %.3f s\n",
+ // CPU time
+ $endUsage['ru_utime.tv_sec'] + $endUsage['ru_utime.tv_usec'] * 1e-6
+ - $startUsage['ru_utime.tv_sec'] - $startUsage['ru_utime.tv_usec'] * 1e-6,
+ // Wall clock time
+ $endTime - $startTime );
+ }
+
+ /**
+ * Fetch the ID of the revision of a Title that occurred
+ *
+ * @param Title $title
+ * @param string $timestamp
+ * @return bool|string Revision ID, or false if not found or error
+ */
+ function getRevIdForTime( Title $title, $timestamp ) {
+ $dbr = wfGetDB( DB_SLAVE );
+
+ $id = $dbr->selectField(
+ array( 'revision', 'page' ),
+ 'rev_id',
+ array(
+ 'page_namespace' => $title->getNamespace(),
+ 'page_title' => $title->getDBkey(),
+ 'rev_timestamp <= ' . $dbr->addQuotes( $timestamp )
+ ),
+ __METHOD__,
+ array( 'ORDER BY' => 'rev_timestamp DESC', 'LIMIT' => 1 ),
+ array( 'revision' => array( 'INNER JOIN', 'rev_page=page_id' ) )
+ );
+
+ return $id;
+ }
+
+ /**
+ * Parse the text from a given Revision
+ *
+ * @param Revision $revision
+ */
+ function runParser( Revision $revision ) {
+ $content = $revision->getContent();
+ $content->getParserOutput( $revision->getTitle(), $revision->getId() );
+ }
+
+ /**
+ * Hook into the parser's revision ID fetcher. Make sure that the parser only
+ * uses revisions around the specified timestamp.
+ *
+ * @param Parser $parser
+ * @param Title $title
+ * @param bool &$skip
+ * @param string|bool &$id
+ * @return bool
+ */
+ function onFetchTemplate( Parser $parser, Title $title, &$skip, &$id ) {
+ $pdbk = $title->getPrefixedDBkey();
+ if ( !isset( $this->idCache[$pdbk] ) ) {
+ $proposedId = $this->getRevIdForTime( $title, $this->templateTimestamp );
+ $this->idCache[$pdbk] = $proposedId;
+ }
+ if ( $this->idCache[$pdbk] !== false ) {
+ $id = $this->idCache[$pdbk];
+ }
+
+ return true;
+ }
+}
+
+$maintClass = 'BenchmarkParse';
+require RUN_MAINTENANCE_IF_MAIN;
'undo-success',
'undo-failure',
'undo-norev',
+ 'undo-nochange',
'undo-summary',
'undo-summary-username-hidden',
),
parent::__construct();
$this->addOption( 'list', 'List special page names' );
$this->addOption( 'only', 'Only update "page"; case sensitive, ' .
- 'check correct case by calling this script with --list or on ' .
- 'includes/QueryPage.php. Ex: --only=BrokenRedirects', false, true );
+ 'check correct case by calling this script with --list or on ' .
+ 'includes/QueryPage.php. Ex: --only=BrokenRedirects', false, true );
$this->addOption( 'override', 'Also update pages that have updates disabled' );
}
public function execute() {
global $IP, $wgQueryPages, $wgQueryCacheLimit, $wgDisableQueryPageUpdate;
- if ( !$this->hasOption( 'list' ) && !$this->hasOption( 'only' ) ) {
- $this->doSpecialPageCacheUpdates();
- }
$dbw = wfGetDB( DB_MASTER );
+ $this->doSpecialPageCacheUpdates( $dbw );
+
// This is needed to initialise $wgQueryPages
require_once "$IP/includes/QueryPage.php";
# --list : just show the name of pages
if ( $this->hasOption( 'list' ) ) {
- $this->output( "$special\n" );
+ $this->output( "$special [QueryPage]\n" );
continue;
}
- if ( !$this->hasOption( 'override' ) && $wgDisableQueryPageUpdate && in_array( $special, $wgDisableQueryPageUpdate ) ) {
- $this->output( sprintf( "%-30s disabled\n", $special ) );
+ if ( !$this->hasOption( 'override' )
+ && $wgDisableQueryPageUpdate && in_array( $special, $wgDisableQueryPageUpdate ) )
+ {
+ $this->output( sprintf( "%-30s [QueryPage] disabled\n", $special ) );
continue;
}
}
if ( !$this->hasOption( 'only' ) || $this->getOption( 'only' ) == $queryPage->getName() ) {
- $this->output( sprintf( '%-30s ', $special ) );
+ $this->output( sprintf( '%-30s [QueryPage] ', $special ) );
if ( $queryPage->isExpensive() ) {
$t1 = explode( ' ', microtime() );
# Do the query
}
}
- public function doSpecialPageCacheUpdates() {
+ public function doSpecialPageCacheUpdates( $dbw ) {
global $wgSpecialPageCacheUpdates;
- $dbw = wfGetDB( DB_MASTER );
foreach ( $wgSpecialPageCacheUpdates as $special => $call ) {
- if ( !is_callable( $call ) ) {
- $this->error( "Uncallable function $call!" );
+ # --list : just show the name of pages
+ if ( $this->hasOption( 'list' ) ) {
+ $this->output( "$special [callback]\n" );
continue;
}
- $this->output( sprintf( '%-30s ', $special ) );
- $t1 = explode( ' ', microtime() );
- call_user_func( $call, $dbw );
- $t2 = explode( ' ', microtime() );
- $elapsed = ( $t2[0] - $t1[0] ) + ( $t2[1] - $t1[1] );
- $hours = intval( $elapsed / 3600 );
- $minutes = intval( $elapsed % 3600 / 60 );
- $seconds = $elapsed - $hours * 3600 - $minutes * 60;
- if ( $hours ) {
- $this->output( $hours . 'h ' );
- }
- if ( $minutes ) {
- $this->output( $minutes . 'm ' );
+
+ if ( !$this->hasOption( 'only' ) || $this->getOption( 'only' ) == $special ) {
+ if ( !is_callable( $call ) ) {
+ $this->error( "Uncallable function $call!" );
+ continue;
+ }
+ $this->output( sprintf( '%-30s [callback] ', $special ) );
+ $t1 = explode( ' ', microtime() );
+ call_user_func( $call, $dbw );
+ $t2 = explode( ' ', microtime() );
+
+ $this->output( "completed in " );
+ $elapsed = ( $t2[0] - $t1[0] ) + ( $t2[1] - $t1[1] );
+ $hours = intval( $elapsed / 3600 );
+ $minutes = intval( $elapsed % 3600 / 60 );
+ $seconds = $elapsed - $hours * 3600 - $minutes * 60;
+ if ( $hours ) {
+ $this->output( $hours . 'h ' );
+ }
+ if ( $minutes ) {
+ $this->output( $minutes . 'm ' );
+ }
+ $this->output( sprintf( "%.2fs\n", $seconds ) );
+ # Wait for the slave to catch up
+ wfWaitForSlaves();
}
- $this->output( sprintf( "completed in %.2fs\n", $seconds ) );
- # Wait for the slave to catch up
- wfWaitForSlaves();
}
}
}
// Content styling
text-align: center;
font-weight: bold;
- white-space: nowrap;
text-shadow: 0 1px rgba(0, 0, 0, .1);
// Interaction styling
padding-right: 0.4em;
margin: 0;
border: none;
+ background-color: transparent;
background-image: none;
text-indent: 0;
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- version="1.1"
- width="12"
- height="13"
- id="svg2">
- <defs
- id="defs4" />
- <metadata
- id="metadata7">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- transform="translate(-894,-731.57648)"
- id="layer1">
- <path
- d="m -0.75761414,8.9593897 a 5.177032,5.177032 0 1 1 -10.35406386,0 5.177032,5.177032 0 1 1 10.35406386,0 z"
- transform="matrix(0.85040896,0,0,0.83575426,904.36465,729.3551)"
- id="path2996"
- style="fill:none;stroke:#6c6c6c;stroke-width:2.13510537;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- d="m 902.17653,740.39168 2.26569,2.6414"
- id="path3766"
- style="fill:none;stroke:#6c6c6c;stroke-width:2.20000005;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="13">
+ <g stroke-width="2" stroke="#6c6c6c" fill="none">
+ <path d="m11.29 11.71-4-4"/>
+ <circle cx="5" cy="5" r="4"/>
+ </g>
</svg>
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- version="1.1"
- width="12"
- height="13"
- id="svg2">
- <defs
- id="defs4" />
- <metadata
- id="metadata7">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- transform="translate(-894,-731.57648)"
- id="layer1">
- <path
- d="m -0.75761414,8.9593897 a 5.177032,5.177032 0 1 1 -10.35406386,0 5.177032,5.177032 0 1 1 10.35406386,0 z"
- transform="matrix(-0.85040896,0,0,0.83575426,895.63918,729.3551)"
- id="path2996"
- style="fill:none;stroke:#6c6c6c;stroke-width:2.13510537;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- d="m 897.8273,740.39168 -2.26569,2.6414"
- id="path3766"
- style="fill:none;stroke:#6c6c6c;stroke-width:2.20000005;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="13">
+ <g stroke-width="2" stroke="#6c6c6c" fill="none">
+ <path d="m.71 11.71 4-4"/>
+ <circle cx="7" cy="5" r="4"/>
+ </g>
</svg>
div#content {
margin-left: 11em;
- padding: 1.5em 1.5em 1.5em 1.75em;
+ padding: 1.25em 1.5em 1.5em 1.5em;
}
#p-logo {
left: @menu-main-logo-left;
@content-font-color: black;
@content-font-size: 0.8em;
@content-line-height: 1.5em;
-@content-padding: 1.25em 1.5em 1.5em 1.5em;
+@content-padding: 1em;
@content-heading-font-size: 1.6em;
@content-heading-font-family: sans-serif;
@body-background-color: #fff;
* @todo This method should be split into 2 separate tests each with a provider
*/
public function testSecureAndSplit() {
+ $this->setMwGlobals( array(
+ 'wgLocalInterwiki' => 'localtestiw',
+ 'wgHooks' => array(
+ 'InterwikiLoadPrefix' => array(
+ function ( $prefix, &$data ) {
+ if ( $prefix === 'localtestiw' ) {
+ $data = array( 'iw_url' => 'localtestiw' );
+ } elseif ( $prefix === 'remotetestiw' ) {
+ $data = array( 'iw_url' => 'remotetestiw' );
+ }
+ return false;
+ }
+ )
+ )
+ ));
// Valid
foreach ( array(
'Sandbox',
'A~~',
// Length is 256 total, but only title part matters
'Category:' . str_repeat( 'x', 248 ),
- str_repeat( 'x', 252 )
+ str_repeat( 'x', 252 ),
+ // interwiki prefix
+ 'localtestiw: #anchor',
+ 'localtestiw:foo',
+ 'localtestiw: foo # anchor',
+ 'localtestiw: Talk: Sandbox # anchor',
+ 'remotetestiw:',
+ 'remotetestiw: Talk: # anchor',
+ 'remotetestiw: #bar',
+ 'remotetestiw: Talk:',
+ 'remotetestiw: Talk: Foo'
) as $text ) {
$this->assertInstanceOf( 'Title', Title::newFromText( $text ), "Valid: $text" );
}
// Namespace prefix without actual title
'Talk:',
'Category: ',
- 'Category: #bar'
+ 'Category: #bar',
+ // interwiki prefix
+ 'localtestiw:',
+ 'localtestiw: Talk: # anchor',
+ 'localtestiw: Talk:'
) as $text ) {
$this->assertNull( Title::newFromText( $text ), "Invalid: $text" );
}
'testmultiselect-opt2' => 'registered-multiselect',
'testmultiselect-opt3' => 'registered-multiselect',
'testmultiselect-opt4' => 'registered-multiselect',
+ 'special' => 'special',
);
if ( $options === null ) {
$this->assertEquals( self::$Success, $response );
}
+ public function testSpecialOption() {
+ $this->mUserMock->expects( $this->never() )
+ ->method( 'resetOptions' );
+
+ $this->mUserMock->expects( $this->never() )
+ ->method( 'saveSettings' );
+
+ $request = $this->getSampleRequest( array(
+ 'change' => 'special=1'
+ ) );
+
+ $response = $this->executeQuery( $request );
+
+ $this->assertEquals( array(
+ 'options' => 'success',
+ 'warnings' => array(
+ 'options' => array(
+ '*' => "Validation error for 'special': cannot be set by this module"
+ )
+ )
+ ), $response );
+ }
+
public function testUnknownOption() {
$this->mUserMock->expects( $this->never() )
->method( 'resetOptions' );