exception metadata to JSON and logs it to the 'exception-json' log group.
This makes MediaWiki easier to integrate with log aggregation and analysis
tools.
+* $wgSquidServersNoPurge now supports the use of Classless Inter-Domain
+ Routing (CIDR) notation to specify contiguous blocks of IPv4 and/or IPv6
+ addresses that should be trusted to provide X-Forwarded-For headers.
=== New features in 1.23 ===
* ResourceLoader can utilize the Web Storage API to cache modules client-side.
get the label, and "$key-value" or "$key-value-text"/"$key-value-html" to
format the value.
$key: Key for the limit report item (string)
-$value: Value of the limit report item
+&$value: Value of the limit report item
&$report: String onto which to append the data
$isHTML: If true, $report is an HTML table with two columns; if false, it's
text intended for display in a monospaced font.
'ParserLimitReportPrepare': Called at the end of Parser:parse() when the parser will
include comments about size of the text parsed. Hooks should use
-$output->setLimitReportData() to populate data.
+$output->setLimitReportData() to populate data. Functions for this hook should
+not use $wgLang; do that in ParserLimitReportFormat instead.
$parser: Parser object
$output: ParserOutput object
/**
* As above, except these servers aren't purged on page changes; use to set a
- * list of trusted proxies, etc.
+ * list of trusted proxies, etc. Supports both individual IP addresses and
+ * CIDR blocks.
*/
$wgSquidServersNoPurge = array();
foreach ( $output->getLimitReportData() as $key => $value ) {
if ( wfRunHooks( 'ParserLimitReportFormat',
- array( $key, $value, &$limitReport, true, true )
+ array( $key, &$value, &$limitReport, true, true )
) ) {
$keyMsg = wfMessage( $key );
$valueMsg = wfMessage( array( "$key-value-html", "$key-value" ) );
*
* Copyright © 2008, Niklas Laxström
* Copyright © 2011, Antoine Musso
+ * Copyright © 2013, Bartosz Dziewoński
*
* 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
const STRING = 0;
/** Integer type, maps guessType() to WebRequest::getInt() */
const INT = 1;
+ /** Float type, maps guessType() to WebRequest::getFloat()
+ * @since 1.23 */
+ const FLOAT = 4;
/** Boolean type, maps guessType() to WebRequest::getBool() */
const BOOL = 2;
/** Integer type or null, maps to WebRequest::getIntOrNull()
return self::BOOL;
} elseif ( is_int( $data ) ) {
return self::INT;
+ } elseif ( is_float( $data ) ) {
+ return self::FLOAT;
} elseif ( is_string( $data ) ) {
return self::STRING;
} else {
}
/**
- * Validate and set an option integer value
- * The value will be altered to fit in the range.
+ * @see validateBounds()
+ */
+ public function validateIntBounds( $name, $min, $max ) {
+ $this->validateBounds( $name, $min, $max );
+ }
+
+ /**
+ * Constrain a numeric value for a given option to a given range. The value will be altered to fit
+ * in the range.
*
- * @param string $name option name
- * @param int $min minimum value
- * @param int $max maximum value
+ * @since 1.23
+ *
+ * @param string $name Option name
+ * @param int|float $min Minimum value
+ * @param int|float $max Maximum value
* @throws MWException If option is not of type INT
*/
- public function validateIntBounds( $name, $min, $max ) {
+ public function validateBounds( $name, $min, $max ) {
$this->validateName( $name, true );
+ $type = $this->options[$name]['type'];
- if ( $this->options[$name]['type'] !== self::INT ) {
- throw new MWException( "Option $name is not of type int" );
+ if ( $type !== self::INT && $type !== self::FLOAT ) {
+ throw new MWException( "Option $name is not of type INT or FLOAT" );
}
$value = $this->getValueReal( $this->options[$name] );
case self::INT:
$value = $r->getInt( $name, $default );
break;
+ case self::FLOAT:
+ $value = $r->getFloat( $name, $default );
+ break;
case self::STRING:
$value = $r->getText( $name, $default );
break;
if ( $useLogPipe ) {
$desc[3] = array( 'pipe', 'w' );
}
+
+ # TODO/FIXME: This is a bad hack to workaround an HHVM bug that prevents
+ # proc_open() from opening stdin/stdout, so use /dev/null *for now*
+ # See bug 56597 / https://github.com/facebook/hhvm/issues/1247 for more info
+ if ( wfIsHHVM() ) {
+ $desc[0] = array( 'file', '/dev/null', 'r' );
+ $desc[2] = array( 'file', '/dev/null', 'w' );
+ }
+
$pipes = null;
$proc = proc_open( $cmd, $desc, $pipes );
if ( !$proc ) {
*/
function wfIsConfiguredProxy( $ip ) {
global $wgSquidServers, $wgSquidServersNoPurge;
- $trusted = in_array( $ip, $wgSquidServers ) ||
- in_array( $ip, $wgSquidServersNoPurge );
+
+ // quick check of known proxy servers
+ $trusted = in_array( $ip, $wgSquidServers );
+
+ if ( !$trusted ) {
+ // slightly slower check to see if the ip is listed directly or in a CIDR
+ // block in $wgSquidServersNoPurge
+ foreach ( $wgSquidServersNoPurge as $block ) {
+ if ( IP::isInRange( $ip, $block ) ) {
+ $trusted = true;
+ break;
+ }
+ }
+ }
return $trusted;
}
: null;
}
+ /**
+ * Fetch a floating point value from the input or return $default if not set.
+ * Guaranteed to return a float; non-numeric input will typically
+ * return 0.
+ *
+ * @since 1.23
+ * @param $name String
+ * @param $default Float
+ * @return Float
+ */
+ public function getFloat( $name, $default = 0 ) {
+ return floatval( $this->getVal( $name, $default ) );
+ }
+
/**
* Fetch a boolean value from the input or return $default if not set.
* Guaranteed to return true or false, with normal PHP semantics for
* @param string $watchlist Valid values: 'watch', 'unwatch', 'preferences', 'nochange'
* @param $titleObj Title the page under consideration
* @param string $userOption The user option to consider when $watchlist=preferences.
- * If not set will magically default to either watchdefault or watchcreations
+ * If not set will use watchdefault always and watchcreations if $titleObj doesn't exist.
* @return bool
*/
protected function getWatchlistValue( $watchlist, $titleObj, $userOption = null ) {
if ( $userWatching ) {
return true;
}
- # If no user option was passed, use watchdefault or watchcreations
+ # If no user option was passed, use watchdefault and watchcreations
if ( is_null( $userOption ) ) {
- $userOption = $titleObj->exists()
- ? 'watchdefault' : 'watchcreations';
+ return $this->getUser()->getBoolOption( 'watchdefault' ) ||
+ $this->getUser()->getBoolOption( 'watchcreations' ) && !$titleObj->exists();
}
# Watch the article based on the user preference
return $this->getUser()->getBoolOption( $userOption );
$cascade = $params['cascade'];
$watch = $params['watch'] ? 'watch' : $params['watchlist'];
- $this->setWatch( $watch, $titleObj );
+ $this->setWatch( $watch, $titleObj, 'watchdefault' );
$status = $pageObj->doUpdateRestrictions( $protections, $expiryarray, $cascade, $params['reason'], $this->getUser() );
if ( $row['iw_local'] == '1' ) {
$val['local'] = '';
}
- // $val['trans'] = intval( $row['iw_trans'] ); // should this be exposed?
+ if ( $row['iw_trans'] == '1' ) {
+ $val['trans'] = '';
+ }
if ( isset( $langNames[$prefix] ) ) {
$val['language'] = $langNames[$prefix];
}
/** @var $file File */
$file = $this->mUpload->getLocalFile();
- $watch = $this->getWatchlistValue( $this->mParams['watchlist'], $file->getTitle() );
+
+ // For preferences mode, we want to watch if 'watchdefault' is set or
+ // if the *file* doesn't exist and 'watchcreations' is set. But
+ // getWatchlistValue()'s automatic handling checks if the *title*
+ // exists or not, so we need to check both prefs manually.
+ $watch = $this->getWatchlistValue(
+ $this->mParams['watchlist'], $file->getTitle(), 'watchdefault'
+ );
+ if ( !$watch && $this->mParams['watchlist'] == 'preferences' && !$file->exists() ) {
+ $watch = $this->getWatchlistValue(
+ $this->mParams['watchlist'], $file->getTitle(), 'watchcreations'
+ );
+ }
// Deprecated parameters
if ( $this->mParams['watch'] ) {
'jquery.makeCollapsible',
'mediawiki.icon',
) );
+
return '';
}
+
/**
* Format a line for enhanced recentchange (aka with javascript and block of lines).
*
wfProfileIn( __METHOD__ );
# Create a specialised object
- $rc = RCCacheEntry::newFromParent( $baseRC );
+ $cacheEntry = RCCacheEntry::newFromParent( $baseRC );
- $curIdEq = array( 'curid' => $rc->mAttribs['rc_cur_id'] );
+ $curIdEq = array( 'curid' => $cacheEntry->mAttribs['rc_cur_id'] );
# If it's a new day, add the headline and flush the cache
- $date = $this->getLanguage()->userDate( $rc->mAttribs['rc_timestamp'], $this->getUser() );
+ $date = $this->getLanguage()->userDate(
+ $cacheEntry->mAttribs['rc_timestamp'],
+ $this->getUser()
+ );
+
$ret = '';
+
if ( $date != $this->lastdate ) {
# Process current cache
$ret = $this->recentChangesBlock();
}
# Should patrol-related stuff be shown?
- $rc->unpatrolled = $this->showAsUnpatrolled( $rc );
+ $cacheEntry->unpatrolled = $this->showAsUnpatrolled( $cacheEntry );
$showdifflinks = true;
+
# Make article link
- $type = $rc->mAttribs['rc_type'];
- $logType = $rc->mAttribs['rc_log_type'];
+ $type = $cacheEntry->mAttribs['rc_type'];
+ $logType = $cacheEntry->mAttribs['rc_log_type'];
+
// Page moves, very old style, not supported anymore
if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
// New unpatrolled pages
- } elseif ( $rc->unpatrolled && $type == RC_NEW ) {
- $clink = Linker::linkKnown( $rc->getTitle() );
+ } elseif ( $cacheEntry->unpatrolled && $type == RC_NEW ) {
+ $clink = Linker::linkKnown( $cacheEntry->getTitle() );
// Log entries
} elseif ( $type == RC_LOG ) {
if ( $logType ) {
$logname = $logpage->getName()->escaped();
$clink = $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $logtitle, $logname ) )->escaped();
} else {
- $clink = Linker::link( $rc->getTitle() );
+ $clink = Linker::link( $cacheEntry->getTitle() );
}
$watched = false;
// Log entries (old format) and special pages
- } elseif ( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) {
+ } elseif ( $cacheEntry->mAttribs['rc_namespace'] == NS_SPECIAL ) {
wfDebug( "Unexpected special page in recentchanges\n" );
$clink = '';
// Edits
} else {
- $clink = Linker::linkKnown( $rc->getTitle() );
+ $clink = Linker::linkKnown( $cacheEntry->getTitle() );
}
# Don't show unusable diff links
- if ( !ChangesList::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) {
+ if ( !ChangesList::userCan( $cacheEntry, Revision::DELETED_TEXT, $this->getUser() ) ) {
$showdifflinks = false;
}
- $time = $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() );
- $rc->watched = $watched;
- $rc->link = $clink;
- $rc->timestamp = $time;
- $rc->numberofWatchingusers = $baseRC->numberofWatchingusers;
+ $time = $this->getLanguage()->userTime( $cacheEntry->mAttribs['rc_timestamp'], $this->getUser() );
+
+ $cacheEntry->watched = $watched;
+ $cacheEntry->link = $clink;
+ $cacheEntry->timestamp = $time;
+ $cacheEntry->numberofWatchingusers = $baseRC->numberofWatchingusers;
# Make "cur" and "diff" links. Do not use link(), it is too slow if
# called too many times (50% of CPU time on RecentChanges!).
- $thisOldid = $rc->mAttribs['rc_this_oldid'];
- $lastOldid = $rc->mAttribs['rc_last_oldid'];
+ $thisOldid = $cacheEntry->mAttribs['rc_this_oldid'];
+ $lastOldid = $cacheEntry->mAttribs['rc_last_oldid'];
$querycur = $curIdEq + array( 'diff' => '0', 'oldid' => $thisOldid );
$querydiff = $curIdEq + array( 'diff' => $thisOldid, 'oldid' => $lastOldid );
if ( $type != RC_NEW ) {
$curLink = $this->message['cur'];
} else {
- $curUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querycur ) );
+ $curUrl = htmlspecialchars( $cacheEntry->getTitle()->getLinkURL( $querycur ) );
$curLink = "<a href=\"$curUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['cur']}</a>";
}
$diffLink = $this->message['diff'];
} else {
- $diffUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querydiff ) );
- $curUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querycur ) );
+ $diffUrl = htmlspecialchars( $cacheEntry->getTitle()->getLinkURL( $querydiff ) );
+ $curUrl = htmlspecialchars( $cacheEntry->getTitle()->getLinkURL( $querycur ) );
$diffLink = "<a href=\"$diffUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['diff']}</a>";
$curLink = "<a href=\"$curUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['cur']}</a>";
}
} elseif ( in_array( $type, array( RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ) ) ) {
$lastLink = $this->message['last'];
} else {
- $lastLink = Linker::linkKnown( $rc->getTitle(), $this->message['last'],
+ $lastLink = Linker::linkKnown( $cacheEntry->getTitle(), $this->message['last'],
array(), $curIdEq + array( 'diff' => $thisOldid, 'oldid' => $lastOldid ) );
}
# Make user links
- if ( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
- $rc->userlink = ' <span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
+ if ( $this->isDeleted( $cacheEntry, Revision::DELETED_USER ) ) {
+ $cacheEntry->userlink = ' <span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
} else {
- $rc->userlink = Linker::userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
- $rc->usertalklink = Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+ $cacheEntry->userlink = Linker::userLink(
+ $cacheEntry->mAttribs['rc_user'],
+ $cacheEntry->mAttribs['rc_user_text']
+ );
+
+ $cacheEntry->usertalklink = Linker::userToolLinks(
+ $cacheEntry->mAttribs['rc_user'],
+ $cacheEntry->mAttribs['rc_user_text']
+ );
}
- $rc->lastlink = $lastLink;
- $rc->curlink = $curLink;
- $rc->difflink = $diffLink;
+ $cacheEntry->lastlink = $lastLink;
+ $cacheEntry->curlink = $curLink;
+ $cacheEntry->difflink = $diffLink;
# Put accumulated information into the cache, for later display
# Page moves go on their own line
- $title = $rc->getTitle();
+ $title = $cacheEntry->getTitle();
$secureName = $title->getPrefixedDBkey();
+
if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
# Use an @ character to prevent collision with page names
- $this->rc_cache['@@' . ( $this->rcMoveIndex++ )] = array( $rc );
+ $this->rc_cache['@@' . ( $this->rcMoveIndex++ )] = array( $cacheEntry );
} else {
# Logs are grouped by type
if ( $type == RC_LOG ) {
$this->rc_cache[$secureName] = array();
}
- array_push( $this->rc_cache[$secureName], $rc );
+ array_push( $this->rc_cache[$secureName], $cacheEntry );
}
wfProfileOut( __METHOD__ );
* Constructor
* @param $context IContextSource context to use, anything else will be ignored
* @param $old Integer old ID we want to show and diff with.
- * @param $new String|int either 'prev' or 'next'. Default: 0.
+ * @param $new String|int either revision ID or 'prev' or 'next'. Default: 0.
* @param $rcid Integer Deprecated, no longer used!
* @param $refreshCache boolean If set, refreshes the diff cache
* @param $unhide boolean If set, allow viewing deleted revs
}
$rollback = '';
- $undoLink = '';
$query = array();
# Carry over 'diffonly' param via navigation links
*
* @param string|bool $otitle Header for old text or false
* @param string|bool $ntitle Header for new text or false
- * @param string $notice
+ * @param string $notice HTML between diff header and body
+ *
* @return bool
*/
function showDiff( $otitle, $ntitle, $notice = '' ) {
/**
* Generate a debug comment indicating diff generating time,
* server node, and generator backend.
+ *
+ * @param String $generator: What diff engine was used
+ *
* @return string
*/
protected function debug( $generator = "internal" ) {
$data[] = wfHostname();
}
$data[] = wfTimestamp( TS_DB );
- return "<!-- diff generator: " .
- implode( " ",
- array_map(
+ return "<!-- diff generator: "
+ . implode( " ",
+ array_map(
"htmlspecialchars",
- $data ) ) .
- " -->\n";
+ $data )
+ )
+ . " -->\n";
}
/**
* Replace line numbers with the text in the user's language
+ *
+ * @param String $text
+ *
* @return mixed
*/
function localiseLineNumbers( $text ) {
/**
* Add the header to a diff body
*
+ * @param String $diff: Diff body
+ * @param String $otitle: Old revision header
+ * @param String $ntitle: New revision header
+ * @param String $multi: Notice telling user that there are intermediate revisions between the ones being compared
+ * @param String $notice: Other notices, e.g. that user is viewing deleted content
+ *
* @return string
*/
function addHeader( $diff, $otitle, $ntitle, $multi = '', $notice = '' ) {
protected $maxFileSize = 4294967296; // integer bytes (4GiB)
const CACHE_TTL = 10; // integer; TTL in seconds for process cache entries
- const CACHE_CHEAP_SIZE = 300; // integer; max entries in "cheap cache"
+ const CACHE_CHEAP_SIZE = 500; // integer; max entries in "cheap cache"
const CACHE_EXPENSIVE_SIZE = 5; // integer; max entries in "expensive cache"
/**
*
* @param string $fullCont Resolved container name
* @param string $dir Resolved storage directory with no trailing slash
- * @param string|null $after Storage path of file to list items after
+ * @param string|null $after Resolved container relative path to list items after
* @param integer $limit Max number of items to list
* @param array $params Parameters for getDirectoryList()
- * @return Array List of resolved paths of directories directly under $dir
+ * @return Array List of container relative resolved paths of directories directly under $dir
* @throws FileBackendError
*/
public function getDirListPageInternal( $fullCont, $dir, &$after, $limit, array $params ) {
*
* @param string $fullCont Resolved container name
* @param string $dir Resolved storage directory with no trailing slash
- * @param string|null $after Storage path of file to list items after
+ * @param string|null $after Resolved container relative path of file to list items after
* @param integer $limit Max number of items to list
* @param array $params Parameters for getDirectoryList()
- * @return Array List of resolved paths of files under $dir
+ * @return Array List of resolved container relative paths of files under $dir
* @throws FileBackendError
*/
public function getFileListPageInternal( $fullCont, $dir, &$after, $limit, array $params ) {
- $files = array();
+ $files = array(); // list of (path, stat array or null) entries
if ( $after === INF ) {
return $files; // nothing more
}
try {
$container = $this->getContainer( $fullCont );
$prefix = ( $dir == '' ) ? null : "{$dir}/";
+ $objects = array(); // list of unfiltered names or CF_Object items
// Non-recursive: only list files right under $dir
- if ( !empty( $params['topOnly'] ) ) { // files and dirs
+ if ( !empty( $params['topOnly'] ) ) {
if ( !empty( $params['adviseStat'] ) ) {
- $limit = min( $limit, self::CACHE_CHEAP_SIZE );
// Note: get_objects() does not include directories
- $objects = $this->loadObjectListing( $params, $dir,
- $container->get_objects( $limit, $after, $prefix, null, '/' ) );
- $files = $objects;
+ $objects = $container->get_objects( $limit, $after, $prefix, null, '/' );
} else {
+ // Note: list_objects() includes directories here
$objects = $container->list_objects( $limit, $after, $prefix, null, '/' );
- foreach ( $objects as $object ) { // files and directories
- if ( substr( $object, -1 ) !== '/' ) {
- $files[] = $object; // directories end in '/'
- }
- }
}
+ $files = $this->buildFileObjectListing( $params, $dir, $objects );
// Recursive: list all files under $dir and its subdirs
- } else { // files
+ } else {
+ // Note: get_objects()/list_objects() here only return file objects
if ( !empty( $params['adviseStat'] ) ) {
- $limit = min( $limit, self::CACHE_CHEAP_SIZE );
- $objects = $this->loadObjectListing( $params, $dir,
- $container->get_objects( $limit, $after, $prefix ) );
+ $objects = $container->get_objects( $limit, $after, $prefix );
} else {
$objects = $container->list_objects( $limit, $after, $prefix );
}
- $files = $objects;
+ $files = $this->buildFileObjectListing( $params, $dir, $objects );
}
// Page on the unfiltered object listing (what is returned may be filtered)
if ( count( $objects ) < $limit ) {
$after = INF; // avoid a second RTT
} else {
$after = end( $objects ); // update last item
+ $after = is_object( $after ) ? $after->name : $after;
}
} catch ( NoSuchContainerException $e ) {
} catch ( CloudFilesException $e ) { // some other exception?
}
/**
- * Load a list of objects that belong under $dir into stat cache
- * and return a list of the names of the objects in the same order.
+ * Build a list of file objects, filtering out any directories
+ * and extracting any stat info if provided in $objects (for CF_Objects)
*
* @param array $params Parameters for getDirectoryList()
* @param string $dir Resolved container directory path
- * @param array $cfObjects List of CF_Object items
- * @return array List of object names
+ * @param array $objects List of CF_Object items or object names
+ * @return array List of (names,stat array or null) entries
*/
- private function loadObjectListing( array $params, $dir, array $cfObjects ) {
+ private function buildFileObjectListing( array $params, $dir, array $objects ) {
$names = array();
- $storageDir = rtrim( $params['dir'], '/' );
- $suffixStart = ( $dir === '' ) ? 0 : strlen( $dir ) + 1; // size of "path/to/dir/"
- // Iterate over the list *backwards* as this primes the stat cache, which is LRU.
- // If this fills the cache and the caller stats an uncached file before stating
- // the ones on the listing, there would be zero cache hits if this went forwards.
- for ( end( $cfObjects ); key( $cfObjects ) !== null; prev( $cfObjects ) ) {
- $object = current( $cfObjects );
- $path = "{$storageDir}/" . substr( $object->name, $suffixStart );
- $val = array(
- // Convert various random Swift dates to TS_MW
- 'mtime' => $this->convertSwiftDate( $object->last_modified, TS_MW ),
- 'size' => (int)$object->content_length,
- 'latest' => false // eventually consistent
- );
- $this->cheapCache->set( $path, 'stat', $val );
- $names[] = $object->name;
+ foreach ( $objects as $object ) {
+ if ( is_object( $object ) ) {
+ $stat = array(
+ // Convert various random Swift dates to TS_MW
+ 'mtime' => $this->convertSwiftDate( $object->last_modified, TS_MW ),
+ 'size' => (int)$object->content_length,
+ 'latest' => false // eventually consistent
+ );
+ $names[] = array( $object->name, $stat );
+ } elseif ( substr( $object, -1 ) !== '/' ) {
+ // Omit directories, which end in '/' in listings
+ $names[] = array( $object, null );
+ }
}
- return array_reverse( $names ); // keep the paths in original order
+ return $names;
+ }
+
+ /**
+ * Do not call this function outside of SwiftFileBackendFileList
+ *
+ * @param string $path Storage path
+ * @param array $val Stat value
+ * @return void
+ */
+ public function loadListingStatInternal( $path, array $val ) {
+ $this->cheapCache->set( $path, 'stat', $val );
}
protected function doGetFileSha1base36( array $params ) {
* @ingroup FileBackend
*/
abstract class SwiftFileBackendList implements Iterator {
- /** @var Array */
+ /** @var Array List of path or (path,stat array) entries */
protected $bufferIter = array();
protected $bufferAfter = null; // string; list items *after* this path
protected $pos = 0; // integer
* @return string|bool String (relative path) or false
*/
public function current() {
- return substr( current( $this->bufferIter ), $this->suffixStart );
+ list( $path, $stat ) = current( $this->bufferIter );
+ $relPath = substr( $path, $this->suffixStart );
+ if ( is_array( $stat ) ) {
+ $storageDir = rtrim( $this->params['dir'], '/' );
+ $this->backend->loadListingStatInternal( "$storageDir/$path", $stat );
+ }
+ return $relPath;
}
/**
if ( !$this->db->tableExists( 'user_rights', __METHOD__ ) ) {
if ( $this->db->fieldExists( 'user', 'user_rights', __METHOD__ ) ) {
- $this->db->applyPatch(
+ $this->applyPatch(
'patch-user_rights.sql',
false,
'Upgrading from a 1.3 or older database? Breaking out user_rights for conversion'
# Transform functions and binaries need a FS source file
$scalerParams['srcPath'] = $image->getLocalRefPath();
+ if ( $scalerParams['srcPath'] === false ) { // Failed to get local copy
+ wfDebugLog( 'thumbnail',
+ sprintf( 'Thumbnail failed on %s: could not get local copy of "%s"',
+ wfHostname(), $image->getName() ) );
+ return new MediaTransformError( 'thumbnail_error',
+ $scalerParams['clientWidth'], $scalerParams['clientHeight'] );
+ }
# Try a hook
$mto = null;
}
foreach ( $this->mOutput->getLimitReportData() as $key => $value ) {
if ( wfRunHooks( 'ParserLimitReportFormat',
- array( $key, $value, &$limitReport, false, false )
+ array( $key, &$value, &$limitReport, false, false )
) ) {
$keyMsg = wfMessage( $key )->inLanguage( 'en' )->useDatabase( false );
$valueMsg = wfMessage( array( "$key-value-text", "$key-value" ) )
$user = $this->getUser();
$request = $this->getRequest();
- if ( !$request->wasPosted() && !$user->isLoggedIn() ) {
+ if ( !$user->isLoggedIn() ) {
$this->error( 'changeemail-no-info' );
return;
/** User input variables from the root section **/
public $mIgnoreWarning;
- public $mWatchThis;
+ public $mWatchthis;
public $mCopyrightStatus;
public $mCopyrightSource;
public $uploadFormTextTop;
public $uploadFormTextAfterSummary;
- public $mWatchthis;
-
/**
* Initialize instance variables from request and create an Upload handler
*/
return true;
}
+ $desiredTitleObj = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName );
+ if ( $desiredTitleObj instanceof Title && $this->getUser()->isWatched( $desiredTitleObj ) ) {
+ // Already watched, don't change that
+ return true;
+ }
+
$local = wfLocalFile( $this->mDesiredDestName );
if ( $local && $local->exists() ) {
// We're uploading a new version of an existing file.
// No creation, so don't watch it if we're not already.
- return $this->getUser()->isWatched( $local->getTitle() );
+ return false;
} else {
// New page should get watched if that's our option.
return $this->getUser()->getOption( 'watchcreations' );
'id' => 'wpWatchthis',
'label-message' => 'watchthisupload',
'section' => 'options',
- 'default' => $user->getOption( 'watchcreations' ),
+ 'default' => $this->mWatch,
)
);
}
// Add feed links
$wlToken = $user->getTokenFromOption( 'watchlisttoken' );
if ( $wlToken ) {
- $this->addFeedLinks( array( 'action' => 'feedwatchlist', 'allrev' => 'allrev',
- 'wlowner' => $user->getName(), 'wltoken' => $wlToken ) );
+ $this->addFeedLinks( array(
+ 'action' => 'feedwatchlist',
+ 'allrev' => 1,
+ 'wlowner' => $user->getName(),
+ 'wltoken' => $wlToken,
+ ) );
}
$this->setHeaders();
$this->outputHeader();
- $output->addSubtitle( $this->msg( 'watchlistfor2', $user->getName()
- )->rawParams( SpecialEditWatchlist::buildTools( null ) ) );
+ $output->addSubtitle(
+ $this->msg( 'watchlistfor2', $user->getName() )
+ ->rawParams( SpecialEditWatchlist::buildTools( null ) )
+ );
$request = $this->getRequest();
$nonRevisionTypes = array( RC_LOG );
wfRunHooks( 'SpecialWatchlistGetNonRevisionTypes', array( &$nonRevisionTypes ) );
if ( $nonRevisionTypes ) {
- if ( count( $nonRevisionTypes ) === 1 ) {
- // if only one use an equality instead of IN condition
- $nonRevisionTypes = reset( $nonRevisionTypes );
- }
$conds[] = $dbr->makeList(
array(
'rc_this_oldid=page_latest',
* Class to replace regex matches with a string similar to that used in preg_replace()
*/
class RegexlikeReplacer extends Replacer {
- var $r;
+ private $r;
/**
* @param string $r
* Supports lazy initialisation of FSS resource
*/
class ReplacementArray {
- /*mostly private*/ var $data = false;
- /*mostly private*/ var $fss = false;
+ private $data = false;
+ private $fss = false;
/**
* Create an object with the specified replacement array
*/
class ExplodeIterator implements Iterator {
// The subject string
- var $subject, $subjectLength;
+ private $subject, $subjectLength;
// The delimiter
- var $delim, $delimLength;
+ private $delim, $delimLength;
// The position of the start of the line
- var $curPos;
+ private $curPos;
// The position after the end of the next delimiter
- var $endPos;
+ private $endPos;
// The current token
- var $current;
+ private $current;
/**
* Construct a DelimIterator
$nlink = htmlspecialchars( $next );
} else {
$nlink = $this->numLink( $title, $offset + $limit, $limit,
- $query, $next, 'prevn-title', 'mw-nextlink' );
+ $query, $next, 'nextn-title', 'mw-nextlink' );
}
# Make links to set number of items per page
// Delete all the corresponding thumbnails...
$dir = $tempRepo->getZonePath( 'thumb' );
- $iterator = $tempRepo->getBackend()->getFileList( array( 'dir' => $dir ) );
+ $iterator = $tempRepo->getBackend()->getFileList( array( 'dir' => $dir, 'adviseStat' => 1 ) );
$this->output( "Deleting old thumbnails...\n" );
$i = 0;
foreach ( $iterator as $file ) {
--- /dev/null
+<?php
+/**
+ * Convenience maintenance script wrapper, useful for scripts
+ * or extensions located outside of standard locations.
+ *
+ * To use, give the maintenance script as a relative or full path.
+ *
+ * Example usage:
+ *
+ * If your pwd is mediawiki base folder:
+ * php maintenance/runScript.php extensions/Wikibase/lib/maintenance/dispatchChanges.php
+ *
+ * If your pwd is maintenance folder:
+ * php runScript.php ../extensions/Wikibase/lib/maintenance/dispatchChanges.php
+ *
+ * Or full path:
+ * php /var/www/mediawiki/maintenance/runScript.php maintenance/runJobs.php
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @author Katie Filbert < aude.wiki@gmail.com >
+ * @file
+ * @ingroup Maintenance
+ */
+$IP = getenv( 'MW_INSTALL_PATH' );
+
+if ( $IP === false ) {
+ $IP = dirname( __DIR__ );
+
+ putenv( "MW_INSTALL_PATH=$IP" );
+}
+
+require_once "$IP/maintenance/Maintenance.php";
+
+if ( !isset( $argv[1] ) ) {
+ fwrite( STDERR, "This script requires a maintainance script as an argument.\n"
+ . "Usage: runScript.php extensions/Wikibase/lib/maintenance/dispatchChanges\n" );
+ exit( 1 );
+}
+
+$scriptFilename = $argv[1];
+array_shift( $argv );
+
+$scriptFile = realpath( $scriptFilename );
+
+if ( !$scriptFile ) {
+ fwrite( STDERR, "The MediaWiki script file \"{$scriptFilename}\" does not exist.\n" );
+ exit( 1 );
+}
+
+require_once $scriptFile;
*/
addCSS: function ( text ) {
var s = mw.loader.addStyleTag( text );
- return s.sheet || s;
+ return s.sheet || s.styleSheet || s;
},
/**
$this->object->add( 'string1', 'string one' );
$this->object->add( 'string2', 'string two' );
$this->object->add( 'integer', 0 );
+ $this->object->add( 'float', 0.0 );
$this->object->add( 'intnull', 0, FormOptions::INTNULL );
}
private function assertGuessInt( $data ) {
$this->guess( FormOptions::INT, $data );
}
+ private function assertGuessFloat( $data ) {
+ $this->guess( FormOptions::FLOAT, $data );
+ }
private function assertGuessString( $data ) {
$this->guess( FormOptions::STRING, $data );
}
$this->assertGuessInt( 5 );
$this->assertGuessInt( 0x0F );
+ $this->assertGuessFloat( 0.0 );
+ $this->assertGuessFloat( 1.5 );
+ $this->assertGuessFloat( 1e3 );
+
$this->assertGuessString( 'true' );
$this->assertGuessString( 'false' );
$this->assertGuessString( '5' );
$this->assertGuessString( '0' );
+ $this->assertGuessString( '1.5' );
}
/**
false,
'With X-Forwaded-For and private IP and hook (disallowed)'
),
+ array(
+ '12.0.0.1',
+ array(
+ 'REMOTE_ADDR' => 'abcd:0001:002:03:4:555:6666:7777',
+ 'HTTP_X_FORWARDED_FOR' => '12.0.0.1, abcd:0001:002:03:4:555:6666:7777',
+ ),
+ array( 'ABCD:1:2:3::/64' ),
+ array(),
+ false,
+ 'IPv6 CIDR'
+ ),
+ array(
+ '12.0.0.3',
+ array(
+ 'REMOTE_ADDR' => '12.0.0.1',
+ 'HTTP_X_FORWARDED_FOR' => '12.0.0.3, 12.0.0.2'
+ ),
+ array( '12.0.0.0/24' ),
+ array(),
+ false,
+ 'IPv4 CIDR'
+ ),
);
}
* @covers WebRequest::getIP
*/
public function testGetIpLackOfRemoteAddrThrowAnException() {
+ // ensure that local install state doesn't interfere with test
+ $this->setMwGlobals( array(
+ 'wgSquidServersNoPurge' => array(),
+ 'wgSquidServers' => array(),
+ 'wgUsePrivateIPs' => false,
+ 'wgHooks' => array(),
+ ) );
+
$request = new WebRequest();
# Next call throw an exception about lacking an IP
$request->getIP();