Change notes from older releases. For current info see RELEASE-NOTES-1.20.
== MediaWiki 1.18 ==
-=== Changes since 1.18.1 ===
+=== Changes since 1.18.2 ===
+* (bug 35446) Using "{{nse:}}" with an invalid namespace name no longer throws
+ a PHP warning.
+
+== MediaWiki 1.18.2 ==
+2012-03-21
+
+This is a maintenance and security release of the MediaWiki 1.18 branch.
+=== Changes since 1.18.1 ===
* (bug 33686) could not get a list of contributor for an article when using
a SQLite database.
* (Bug 33865) Exception thrown in action=parse when attempting to use the title
parameter without setting the text parameter.
* UserMailer could potentially throw a fatal error when a MailAddress object had
an empty email address.
+* (Bug 33087) Exchange server rejected mail sent by MediaWiki
* (bug 34528) Edit section tooltips show correction section name again
* (bug 34246) MediaWiki:Whatlinkshere-summary message is displayed again in
Special:Whatlinkshere
-
-=== MediaWiki 1.18.1 ===
+* (bug 22555) Remove or skip strip markers from tag hooks like <nowiki> in
+ core parser functions which operate on strings, such as formatnum.
+* (bug 34212) ApiBlock/ApiUnblock allow action to take place without a token
+ parameter present.
+* (bug 34907) Fixed exposure of tokens through load.php that could have facilitated
+ CSRF attacks.
+* (bug 35317) CSRF in Special:Upload.
+
+== MediaWiki 1.18.1 ==
2012-01-11
This a maintenance and security release of the MediaWiki 1.18 branch.
-==== Security changes ====
-* (bug 33117) prop=revisions allows deleted text to be exposed through cache pollution.
-
-==== Changes since 1.18.0 ====
+=== Changes since 1.18.0 ===
* (bug 32712) Fix for search indexing of pages with certain unicode chars following URL.
* (bug 3901) Lang, hreflang attribs added to sidebar interlanguage links for screen readers.
* (bug 30774) mediawiki.html: Add support for numbers and booleans in the
all pages.
* Fixed recentchanges FK violation on page delete and cache purge error in updater
for Oracle DB.
+* (bug 33117) prop=revisions allows deleted text to be exposed through cache pollution.
+
+== MediaWiki 1.18.0 ==
+2011-11-24
+
+This is the first stable release of the MediaWiki 1.18 branch.
=== Summary of selected changes in 1.18 ===
know what you think of it. Beta releases are not recommended for use in
production.
+=== Changes since 1.19 beta 2 ===
+* Special:Watchlist no longer sets links to feed when the user is anonymous
+
=== Changes since 1.19 beta 1 ===
* (bug 35014) Including a special page no longer sets the page's title to the
included page.
inside a heading.
* (bug 34907) Fixed exposure of tokens through load.php that could have facilitated
CSRF attacks
-* Special:Watchlist no longer sets links to feed when the user is anonymous
=== Configuration changes in 1.19 ===
* Removed SkinTemplateSetupPageCss hook; use BeforePageDisplay instead.
),
);
-/**
- * Whether to embed private modules inline with HTML output or to bypass
- * caching and check the user parameter against $wgUser to prevent
- * unauthorized access to private modules.
- */
-$wgResourceLoaderInlinePrivateModules = true;
-
/**
* The default debug mode (on/off) for of ResourceLoader requests. This will still
* be overridden when the debug URL parameter is used.
/**
* Show "your edit contains spam" page with your diff and text
*
- * @param $match string|bool Text which triggered one or more filters
+ * @param $match string|Array|bool Text (or array of texts) which triggered one or more filters
*/
public function spamPageWithContent( $match = false ) {
- global $wgOut;
+ global $wgOut, $wgLang;
$this->textbox2 = $this->textbox1;
+ if( is_array( $match ) ){
+ $match = $wgLang->listToText( $match );
+ }
$wgOut->prepareErrorPage( wfMessage( 'spamprotectiontitle' ) );
$wgOut->addHTML( '<div id="spamprotected">' );
$this->filename = $file;
}
+ function writeCloseStream( $string ) {
+ parent::writeCloseStream( $string );
+ fclose( $this->handle );
+ }
+
function write( $string ) {
fputs( $this->handle, $string );
}
$this->filename = $file;
}
+ function writeCloseStream( $string ) {
+ parent::writeCloseStream( $string );
+ proc_close( $this->procOpenResource );
+ }
+
function startCommand( $command ) {
$spec = array(
0 => array( "pipe", "r" ),
* @return string html <script> and <style> tags
*/
protected function makeResourceLoaderLink( $modules, $only, $useESI = false, array $extraQuery = array(), $loadCall = false ) {
- global $wgResourceLoaderUseESI, $wgResourceLoaderInlinePrivateModules;
+ global $wgResourceLoaderUseESI;
if ( !count( $modules ) ) {
return '';
continue;
}
- // Support inlining of private modules if configured as such. Note that these
- // modules should be loaded from getHeadScripts() before the first loader call.
- // Otherwise other modules can't properly use them as dependencies (bug 30914)
- if ( $group === 'private' && $wgResourceLoaderInlinePrivateModules ) {
+ // Inline private modules. These can't be loaded through load.php for security
+ // reasons, see bug 34907. Note that these modules should be loaded from
+ // getHeadScripts() before the first loader call. Otherwise other modules can't
+ // properly use them as dependencies (bug 30914)
+ if ( $group === 'private' ) {
if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
$links .= Html::inlineStyle(
$resourceLoader->makeModuleResponse( $context, $modules )
}
protected function getDescription() {
- return wfMsgHtml( 'creditspage' );
+ return $this->msg( 'creditspage' )->escaped();
}
/**
}
protected function alterForm( HTMLForm $form ) {
- $form->setSubmitText( wfMsg( 'confirm_purge_button' ) );
+ $form->setSubmitTextMsg( 'confirm_purge_button' );
}
protected function preText() {
- return wfMessage( 'confirm-purge-top' )->parse();
+ return $this->msg( 'confirm-purge-top' )->parse();
}
protected function postText() {
- return wfMessage( 'confirm-purge-bottom' )->parse();
+ return $this->msg( 'confirm-purge-bottom' )->parse();
}
public function onSuccess() {
if ( is_numeric( $k ) ) {
$msg .= " $v\n";
} else {
- $v .= ":";
if ( is_array( $v ) ) {
$msgExample = implode( "\n", array_map( array( $this, 'indentExampleText' ), $v ) );
} else {
$msgExample = " $v";
}
+ $msgExample .= ":";
$msg .= wordwrap( $msgExample, 100, "\n" ) . "\n $k\n";
}
}
// Die if token required, but not provided (unless there is a gettoken parameter)
$salt = $module->getTokenSalt();
- if ( $salt !== false && !isset( $moduleParams['gettoken'] ) ) {
+ if ( $salt !== false && !$moduleParams['gettoken'] ) {
if ( !isset( $moduleParams['token'] ) ) {
$this->dieUsageMsg( array( 'missingparam', 'token' ) );
} else {
}
static function nse( $parser, $part1 = '' ) {
- return wfUrlencode( str_replace( ' ', '_', self::ns( $parser, $part1 ) ) );
+ $ret = self::ns( $parser, $part1 );
+ if ( is_string( $ret ) ) {
+ $ret = wfUrlencode( str_replace( ' ', '_', $ret ) );
+ }
+ return $ret;
}
/**
$cache->set( $key, $result );
} catch ( Exception $exception ) {
// Return exception as a comment
- $result = "/*\n{$exception->__toString()}\n*/\n";
+ $result = $this->makeComment( $exception->__toString() );
}
wfProfileOut( __METHOD__ );
ob_start();
wfProfileIn( __METHOD__ );
- $exceptions = '';
+ $errors = '';
// Split requested modules into two groups, modules and missing
$modules = array();
$missing = array();
foreach ( $context->getModules() as $name ) {
if ( isset( $this->moduleInfos[$name] ) ) {
+ $module = $this->getModule( $name );
+ // Do not allow private modules to be loaded from the web.
+ // This is a security issue, see bug 34907.
+ if ( $module->getGroup() === 'private' ) {
+ $errors .= $this->makeComment( "Cannot show private module \"$name\"" );
+ continue;
+ }
$modules[$name] = $this->getModule( $name );
} else {
$missing[] = $name;
$this->preloadModuleInfo( array_keys( $modules ), $context );
} catch( Exception $e ) {
// Add exception to the output as a comment
- $exceptions .= "/*\n{$e->__toString()}\n*/\n";
+ $errors .= $this->makeComment( $e->__toString() );
}
wfProfileIn( __METHOD__.'-getModifiedTime' );
- $private = false;
// To send Last-Modified and support If-Modified-Since, we need to detect
// the last modified time
$mtime = wfTimestamp( TS_UNIX, $wgCacheEpoch );
* @var $module ResourceLoaderModule
*/
try {
- // Bypass Squid and other shared caches if the request includes any private modules
- if ( $module->getGroup() === 'private' ) {
- $private = true;
- }
// Calculate maximum modified time
$mtime = max( $mtime, $module->getModifiedTime( $context ) );
} catch ( Exception $e ) {
// Add exception to the output as a comment
- $exceptions .= "/*\n{$e->__toString()}\n*/\n";
+ $errors .= $this->makeComment( $e->__toString() );
}
}
wfProfileOut( __METHOD__.'-getModifiedTime' );
// Send content type and cache related headers
- $this->sendResponseHeaders( $context, $mtime, $private );
+ $this->sendResponseHeaders( $context, $mtime );
// If there's an If-Modified-Since header, respond with a 304 appropriately
if ( $this->tryRespondLastModified( $context, $mtime ) ) {
$response = $this->makeModuleResponse( $context, $modules, $missing );
// Prepend comments indicating exceptions
- $response = $exceptions . $response;
+ $response = $errors . $response;
// Capture any PHP warnings from the output buffer and append them to the
// response in a comment if we're in debug mode.
if ( $context->getDebug() && strlen( $warnings = ob_get_contents() ) ) {
- $response = "/*\n$warnings\n*/\n" . $response;
+ $response = $this->makeComment( $warnings ) . $response;
}
// Remove the output buffer and output the response
ob_end_clean();
echo $response;
- // Save response to file cache unless there are private modules or errors
- if ( isset( $fileCache ) && !$private && !$exceptions && !$missing ) {
+ // Save response to file cache unless there are errors
+ if ( isset( $fileCache ) && !$errors && !$missing ) {
// Cache single modules...and other requests if there are enough hits
if ( ResourceFileCache::useFileCache( $context ) ) {
if ( $fileCache->isCacheWorthy() ) {
* Send content type and last modified headers to the client.
* @param $context ResourceLoaderContext
* @param $mtime string TS_MW timestamp to use for last-modified
- * @param $private bool True iff response contains any private modules
* @return void
*/
- protected function sendResponseHeaders( ResourceLoaderContext $context, $mtime, $private ) {
+ protected function sendResponseHeaders( ResourceLoaderContext $context, $mtime ) {
global $wgResourceLoaderMaxage;
// If a version wasn't specified we need a shorter expiry time for updates
// to propagate to clients quickly
header( 'Cache-Control: private, no-cache, must-revalidate' );
header( 'Pragma: no-cache' );
} else {
- if ( $private ) {
- header( "Cache-Control: private, max-age=$maxage" );
- $exp = $maxage;
- } else {
- header( "Cache-Control: public, max-age=$maxage, s-maxage=$smaxage" );
- $exp = min( $maxage, $smaxage );
- }
+ header( "Cache-Control: public, max-age=$maxage, s-maxage=$smaxage" );
+ $exp = min( $maxage, $smaxage );
header( 'Expires: ' . wfTimestamp( TS_RFC2822, $exp + time() ) );
}
}
return false; // cache miss
}
+ protected function makeComment( $text ) {
+ $encText = str_replace( '*/', '* /', $text );
+ return "/*\n$encText\n*/\n";
+ }
+
/**
* Generates code for a response
*
$blobs = MessageBlobStore::get( $this, $modules, $context->getLanguage() );
} catch ( Exception $e ) {
// Add exception to the output as a comment
- $exceptions .= "/*\n{$e->__toString()}\n*/\n";
+ $exceptions .= $this->makeComment( $e->__toString() );
}
} else {
$blobs = array();
}
} catch ( Exception $e ) {
// Add exception to the output as a comment
- $exceptions .= "/*\n{$e->__toString()}\n*/\n";
+ $exceptions .= $this->makeComment( $e->__toString() );
// Register module as missing
$missing[] = $name;
}
global $wgUser;
-
- if ( $context->getUser() === $wgUser->getName() ) {
- return $this->modifiedTime[$hash] = wfTimestamp( TS_UNIX, $wgUser->getTouched() );
- } else {
- return 1;
- }
- }
-
- /**
- * Fetch the context's user options, or if it doesn't match current user,
- * the default options.
- *
- * @param $context ResourceLoaderContext: Context object
- * @return Array: List of user options keyed by option name
- */
- protected function contextUserOptions( ResourceLoaderContext $context ) {
- global $wgUser;
-
- // Verify identity -- this is a private module
- if ( $context->getUser() === $wgUser->getName() ) {
- return $wgUser->getOptions();
- } else {
- return User::getDefaultOptions();
- }
+ return $this->modifiedTime[$hash] = wfTimestamp( TS_UNIX, $wgUser->getTouched() );
}
/**
* @return array
*/
public function getStyles( ResourceLoaderContext $context ) {
- global $wgAllowUserCssPrefs;
+ global $wgAllowUserCssPrefs, $wgUser;
if ( $wgAllowUserCssPrefs ) {
- $options = $this->contextUserOptions( $context );
+ $options = $wgUser->getOptions();
// Build CSS rules
$rules = array();
}
global $wgUser;
-
- if ( $context->getUser() === $wgUser->getName() ) {
- return $this->modifiedTime[$hash] = wfTimestamp( TS_UNIX, $wgUser->getTouched() );
- } else {
- return 1;
- }
- }
-
- /**
- * Fetch the context's user options, or if it doesn't match current user,
- * the default options.
- *
- * @param $context ResourceLoaderContext: Context object
- * @return Array: List of user options keyed by option name
- */
- protected function contextUserOptions( ResourceLoaderContext $context ) {
- global $wgUser;
-
- // Verify identity -- this is a private module
- if ( $context->getUser() === $wgUser->getName() ) {
- return $wgUser->getOptions();
- } else {
- return User::getDefaultOptions();
- }
+ return $this->modifiedTime[$hash] = wfTimestamp( TS_UNIX, $wgUser->getTouched() );
}
/**
* @return string
*/
public function getScript( ResourceLoaderContext $context ) {
+ global $wgUser;
return Xml::encodeJsCall( 'mw.user.options.set',
- array( $this->contextUserOptions( $context ) ) );
+ array( $wgUser->getOptions() ) );
}
/**
// If it was posted check for the token (no remote POST'ing with user credentials)
$token = $request->getVal( 'wpEditToken' );
- if( $this->mSourceType == 'file' && $token == null ) {
- // Skip token check for file uploads as that can't be faked via JS...
- // Some client-side tools don't expect to need to send wpEditToken
- // with their submissions, as that's new in 1.16.
- $this->mTokenOk = true;
- } else {
- $this->mTokenOk = $this->getUser()->matchEditToken( $token );
- }
+ $this->mTokenOk = $this->getUser()->matchEditToken( $token );
$this->uploadFormTextTop = '';
$this->uploadFormTextAfterSummary = '';
var $skipFooter = false; // don't output </mediawiki>
var $startId = 0;
var $endId = 0;
+ var $revStartId = 0;
+ var $revEndId = 0;
var $sink = null; // Output filters
var $stubText = false; // include rev_text_id instead of text; for 2-pass dump
var $dumpUploads = false;