* to reduce disk usage, limits can only be selected from a list.
* The user preference is saved as an array offset in the database, by default
* the offset is set with $wgDefaultUserOptions['imagesize']. Make sure you
- * change it if you alter the array (see bug 8858).
+ * change it if you alter the array (see T10858).
* This is the list of settings the user can choose from:
*/
$wgImageLimits = [
*
* Currently this appears to work fine in all browsers, but it's disabled by
* default because it normalizes id's a bit too aggressively, breaking preexisting
- * content (particularly Cite). See bug 27733, bug 27694, bug 27474.
+ * content (particularly Cite). See T29733, T29694, T29474.
*/
$wgExperimentalHtmlIds = false;
* Attempting to create a redirect to any of the pages in this array
* will make the redirect fail.
* Userlogout is hard-coded, so it does not need to be listed here.
- * (bug 10569) Disallow Mypage and Mytalk as well.
+ * (T12569) Disallow Mypage and Mytalk as well.
*
* As of now, this only checks special pages. Redirects to pages in
* other namespaces cannot be invalidated by this variable.
];
/**
- * Array of IPs which should be excluded from rate limits.
+ * Array of IPs / CIDR ranges which should be excluded from rate limits.
* This may be useful for whitelisting NAT gateways for conferences, etc.
*/
$wgRateLimitsExcludedIPs = [];
/**
* Port where you have HTTPS running
* Supports HTTPS on non-standard ports
- * @see bug 65184
+ * @see T67184
* @since 1.24
*/
$wgHttpsPort = 443;
$this->recreate = false;
// When creating a new section, we can preload a section title by passing it as the
- // preloadtitle parameter in the URL (Bug 13100)
+ // preloadtitle parameter in the URL (T15100)
if ( $this->section == 'new' && $request->getVal( 'preloadtitle' ) ) {
$this->sectiontitle = $request->getVal( 'preloadtitle' );
// Once wpSummary isn't being use for setting section titles, we should delete this.
$revision = $this->mArticle->getRevisionFetched();
if ( $revision === null ) {
if ( !$this->contentModel ) {
- $this->contentModel = $this->getTitle()->getContentModel();
+ throw new RuntimeException( 'EditPage contentModel was false' );
}
$handler = ContentHandler::getForModelID( $this->contentModel );
if ( $content === false || $content === null ) {
if ( !$this->contentModel ) {
- $this->contentModel = $this->getTitle()->getContentModel();
+ throw new RuntimeException( 'EditPage contentModel was false' );
}
$handler = ContentHandler::getForModelID( $this->contentModel );
// Don't save a new page if it's blank or if it's a MediaWiki:
// message with content equivalent to default (allow empty pages
- // in this case to disable messages, see bug 50124)
+ // in this case to disable messages, see T52124)
$defaultMessageText = $this->mTitle->getDefaultMessageText();
if ( $this->mTitle->getNamespace() === NS_MEDIAWIKI && $defaultMessageText !== false ) {
$defaultText = $defaultMessageText;
}
# Give a notice if the user is editing a deleted/moved page...
if ( !$this->mTitle->exists() ) {
+ $dbr = wfGetDB( DB_REPLICA );
+
LogEventsList::showLogExtract( $wgOut, [ 'delete', 'move' ], $this->mTitle,
'',
[
'lim' => 10,
- 'conds' => [ "log_action != 'revision'" ],
+ 'conds' => [ 'log_action != ' . $dbr->addQuotes( 'revision' ) ],
'showIfEmpty' => false,
'msgKey' => [ 'recreate-moveddeleted-warn' ]
]
if ( $this->hasPresetSummary ) {
// If a summary has been preset using &summary= we don't want to prompt for
// a different summary. Only prompt for a summary if the summary is blanked.
- // (Bug 17416)
+ // (T19416)
$this->autoSumm = md5( '' );
}
}
if ( $oldid ) {
-
- # $diffText = $de->getDiff( wfMessage( 'revisionasof',
- # $wgLang->timeanddate( $timestamp ),
- # $wgLang->date( $timestamp ),
- # $wgLang->time( $timestamp ) )->text(),
- # wfMessage( 'currentrev' )->text() );
-
$diffText = '';
// Don't bother generating the diff if we won't be able to show it
if ( $wgFeedDiffCutoff > 0 ) {
if ( $html === null ) {
- // Omit large new page diffs, bug 29110
+ // Omit large new page diffs, T31110
// Also use diff link for non-textual content
$diffText = self::getDiffLink( $title, $newid );
} else {
# ThumbnailImage::toHtml() already adds page= onto the end of DjVu URLs
# So we don't need to pass it here in $query. However, the URL for the
- # zoom icon still needs it, so we make a unique query for it. See bug 14771
+ # zoom icon still needs it, so we make a unique query for it. See T16771
$url = $title->getLocalURL( $query );
if ( $page ) {
$url = wfAppendQuery( $url, [ 'page' => $page ] );
if ( $altUserName === false ) {
$altUserName = IP::prettifyIP( $userName );
}
- $classes .= ' mw-anonuserlink'; // Separate link class for anons (bug 43179)
+ $classes .= ' mw-anonuserlink'; // Separate link class for anons (T45179)
} else {
$page = Title::makeTitle( NS_USER, $userName );
}
if ( $userId ) {
// check if the user has an edit
$attribs = [];
+ $attribs['class'] = 'mw-usertoollinks-contribs';
if ( $redContribsWhenNoEdits ) {
if ( intval( $edits ) === 0 && $edits !== 0 ) {
$user = User::newFromId( $userId );
$edits = $user->getEditCount();
}
if ( $edits === 0 ) {
- $attribs['class'] = 'new';
+ $attribs['class'] .= ' new';
}
}
$contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
*/
public static function userTalkLink( $userId, $userText ) {
$userTalkPage = Title::makeTitle( NS_USER_TALK, $userText );
- $userTalkLink = self::link( $userTalkPage, wfMessage( 'talkpagelinktext' )->escaped() );
+ $moreLinkAttribs['class'] = 'mw-usertoollinks-talk';
+ $userTalkLink = self::link( $userTalkPage,
+ wfMessage( 'talkpagelinktext' )->escaped(),
+ $moreLinkAttribs );
return $userTalkLink;
}
*/
public static function blockLink( $userId, $userText ) {
$blockPage = SpecialPage::getTitleFor( 'Block', $userText );
- $blockLink = self::link( $blockPage, wfMessage( 'blocklink' )->escaped() );
+ $moreLinkAttribs['class'] = 'mw-usertoollinks-block';
+ $blockLink = self::link( $blockPage,
+ wfMessage( 'blocklink' )->escaped(),
+ $moreLinkAttribs );
return $blockLink;
}
*/
public static function emailLink( $userId, $userText ) {
$emailPage = SpecialPage::getTitleFor( 'Emailuser', $userText );
- $emailLink = self::link( $emailPage, wfMessage( 'emaillink' )->escaped() );
+ $moreLinkAttribs['class'] = 'mw-usertoollinks-mail';
+ $emailLink = self::link( $emailPage,
+ wfMessage( 'emaillink' )->escaped(),
+ $moreLinkAttribs );
return $emailLink;
}
) {
# Sanitize text a bit:
$comment = str_replace( "\n", " ", $comment );
- # Allow HTML entities (for bug 13815)
+ # Allow HTML entities (for T15815)
$comment = Sanitizer::escapeHtmlAllowEntities( $comment );
# Render autocomments and make links:
$section = str_replace( '[[', '', $section );
$section = str_replace( ']]', '', $section );
- $section = Sanitizer::normalizeSectionNameWhitespace( $section ); # bug 22784
+ $section = Sanitizer::normalizeSectionNameWhitespace( $section ); # T24784
if ( $local ) {
$sectionTitle = Title::newFromText( '#' . $section );
} else {
} else {
$suffix = '';
}
- # bug 7425
+ # T9425
$target = trim( $target );
# Look at the first character
if ( $target != '' && $target[0] === '/' ) {
if ( $context->getRequest()->getBool( 'bot' ) ) {
$query['bot'] = '1';
- $query['hidediff'] = '1'; // bug 15999
+ $query['hidediff'] = '1'; // T17999
}
$disableRollbackEditCount = false;
$wgUseEnotif = $wgEnotifUserTalk || $wgEnotifWatchlist;
} else {
// Disable all other email settings automatically if $wgEnableEmail
- // is set to false. - bug 63678
+ // is set to false. - T65678
$wgAllowHTMLEmail = false;
$wgEmailAuthentication = false; // do not require auth if you're not sending email anyway
$wgEnableUserEmail = false;
// is complete.
define( 'MW_SERVICE_BOOTSTRAP_COMPLETE', 1 );
-// Install a header callback to prevent caching of responses with cookies (T127993)
-if ( !$wgCommandLineMode ) {
- header_register_callback( function () {
- $headers = [];
- foreach ( headers_list() as $header ) {
- list( $name, $value ) = explode( ':', $header, 2 );
- $headers[strtolower( trim( $name ) )][] = trim( $value );
- }
-
- if ( isset( $headers['set-cookie'] ) ) {
- $cacheControl = isset( $headers['cache-control'] )
- ? implode( ', ', $headers['cache-control'] )
- : '';
-
- if ( !preg_match( '/(?:^|,)\s*(?:private|no-cache|no-store)\s*(?:$|,)/i', $cacheControl ) ) {
- header( 'Expires: Thu, 01 Jan 1970 00:00:00 GMT' );
- header( 'Cache-Control: private, max-age=0, s-maxage=0' );
- MediaWiki\Logger\LoggerFactory::getInstance( 'cache-cookies' )->warning(
- 'Cookies set on {url} with Cache-Control "{cache-control}"', [
- 'url' => WebRequest::getGlobalRequestURL(),
- 'cookies' => $headers['set-cookie'],
- 'cache-control' => $cacheControl ?: '<not set>',
- ]
- );
- }
- }
- } );
-}
-
MWExceptionHandler::installHandler();
require_once "$IP/includes/compat/normal/UtfNormalUtil.php";
use MediaWiki\Linker\LinkTarget;
use Wikimedia\Assert\Assert;
+use Wikimedia\Rdbms\LoadBalancer;
/**
* Class performing complex database queries related to WatchedItems.
if ( !isset( $options['start'] ) && !isset( $options['end'] ) ) {
if ( $db->getType() === 'mysql' ) {
// This is an index optimization for mysql
- $conds[] = "rc_timestamp > ''";
+ $conds[] = 'rc_timestamp > ' . $db->addQuotes( '' );
}
}
$conds[] = 'rc_user_text != ' . $db->addQuotes( $options['notByUser'] );
}
- // Avoid brute force searches (bug 17342)
+ // Avoid brute force searches (T19342)
$bitmask = 0;
if ( !$user->isAllowed( 'deletedhistory' ) ) {
$bitmask = Revision::DELETED_USER;
die( 'MediaWiki does not support installations where mbstring.func_overload is non-zero.' );
}
- # bug 15461: Make IE8 turn off content sniffing. Everybody else should ignore this
+ # T17461: Make IE8 turn off content sniffing. Everybody else should ignore this
# We're adding it here so that it's *always* set, even for alternate entry
# points and when $wgOut gets disabled or overridden.
header( 'X-Content-Type-Options: nosniff' );
die( 1 );
}
+# Install a header callback
+MediaWiki\HeaderCallback::register();
+
if ( defined( 'MW_CONFIG_CALLBACK' ) ) {
# Use a callback function to configure MediaWiki
call_user_func( MW_CONFIG_CALLBACK );
use \MediaWiki\MediaWikiServices;
use \Wikimedia\WaitConditionLoop;
use \Wikimedia\Rdbms\TransactionProfiler;
+use Wikimedia\Rdbms\LoadBalancer;
/**
* Class to store objects in the database
$exptime = $this->convertExpiry( $exptime );
$encExpiry = $db->timestamp( $exptime );
}
- // (bug 24425) use a replace if the db supports it instead of
+ // (T26425) use a replace if the db supports it instead of
// delete/insert to avoid clashes with conflicting keynames
$db->update(
$tableName,
], __METHOD__, 'IGNORE' );
if ( $db->affectedRows() == 0 ) {
- // Race condition. See bug 28611
+ // Race condition. See T30611
$newValue = null;
}
} catch ( DBError $e ) {
$ns = $this->getTitle()->getNamespace();
- # Don't index user and user talk pages for blocked users (bug 11443)
+ # Don't index user and user talk pages for blocked users (T13443)
if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
$specificTarget = null;
$vagueTarget = null;
}
if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
- # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
+ # (T16900) site config can override user-defined __INDEX__ or __NOINDEX__
$policy = array_merge(
$policy,
self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
$loggedIn = $this->getContext()->getUser()->isLoggedIn();
if ( $loggedIn || $cache->get( $key ) ) {
$logTypes = [ 'delete', 'move' ];
- $conds = [ "log_action != 'revision'" ];
+
+ $dbr = wfGetDB( DB_REPLICA );
+
+ $conds = [ 'log_action != ' . $dbr->addQuotes( 'revision' ) ];
// Give extensions a chance to hide their (unrelated) log entries
Hooks::run( 'Article::MissingArticleConditions', [ &$conds, $logTypes ] );
LogEventsList::showLogExtract(
}
$out->addModuleStyles( [
- 'filepage', // always show the local local Filepage.css, bug 29277
+ 'filepage', // always show the local local Filepage.css, T31277
'mediawiki.action.view.filepage', // Add MediaWiki styles for a file page
] );
}
// this will get messy.
// The dirmark, however, must not be immediately adjacent
// to the filename, because it can get copied with it.
- // See bug 25277.
+ // See T27277.
// @codingStandardsIgnoreStart Ignore long line
$out->addWikiText( <<<EOT
<div class="fullMedia"><span class="dangerousLink">{$medialink}</span> $dirmark<span class="fileInfo">$longDesc</span></div>
} else {
# Image does not exist
if ( !$this->getId() ) {
+ $dbr = wfGetDB( DB_REPLICA );
+
# No article exists either
# Show deletion log to be consistent with normal articles
LogEventsList::showLogExtract(
$this->getTitle()->getPrefixedText(),
'',
[ 'lim' => 10,
- 'conds' => [ "log_action != 'revision'" ],
+ 'conds' => [ 'log_action != ' . $dbr->addQuotes( 'revision' ) ],
'showIfEmpty' => false,
'msgKey' => [ 'moveddeleted-notice' ]
]
$module = $this->getModule( $name );
if ( $module ) {
// Do not allow private modules to be loaded from the web.
- // This is a security issue, see bug 34907.
+ // This is a security issue, see T36907.
if ( $module->getGroup() === 'private' ) {
$this->logger->debug( "Request for private module '$name' denied" );
$this->errors[] = "Cannot show private module \"$name\"";
* @return void
*/
protected function sendResponseHeaders( ResourceLoaderContext $context, $etag, $errors ) {
+ \MediaWiki\HeaderCallback::warnIfHeadersSent();
$rlMaxage = $this->config->get( 'ResourceLoaderMaxage' );
// Use a short cache expiry so that updates propagate to clients quickly, if:
// - No version specified (shared resources, e.g. stylesheets)
$styles = (array)$styles;
foreach ( $styles as $style ) {
$style = trim( $style );
- // Don't output an empty "@media print { }" block (bug 40498)
+ // Don't output an empty "@media print { }" block (T42498)
if ( $style !== '' ) {
// Transform the media type based on request params and config
// The way that this relies on $wgRequest to propagate request params is slightly evil
*/
public function getLessCompiler( $extraVars = [] ) {
// When called from the installer, it is possible that a required PHP extension
- // is missing (at least for now; see bug 47564). If this is the case, throw an
+ // is missing (at least for now; see T49564). If this is the case, throw an
// exception (caught by the installer) to prevent a fatal error later on.
if ( !class_exists( 'Less_Parser' ) ) {
throw new MWException( 'MediaWiki requires the less.php parser' );
* @param array $localFileRefs List of files
*/
protected function saveFileDependencies( ResourceLoaderContext $context, $localFileRefs ) {
- // Normalise array
- $localFileRefs = array_values( array_unique( $localFileRefs ) );
- sort( $localFileRefs );
try {
+ // Related bugs and performance considerations:
+ // 1. Don't needlessly change the database value with the same list in a
+ // different order or with duplicates.
+ // 2. Use relative paths to avoid ghost entries when $IP changes. (T111481)
+ // 3. Don't needlessly replace the database with the same value
+ // just because $IP changed (e.g. when upgrading a wiki).
+ // 4. Don't create an endless replace loop on every request for this
+ // module when '../' is used anywhere. Even though both are expanded
+ // (one expanded by getFileDependencies from the DB, the other is
+ // still raw as originally read by RL), the latter has not
+ // been normalized yet.
+
+ // Normalise
+ $localFileRefs = array_values( array_unique( $localFileRefs ) );
+ sort( $localFileRefs );
+ $localPaths = self::getRelativePaths( $localFileRefs );
+
+ $storedPaths = self::getRelativePaths( $this->getFileDependencies( $context ) );
// If the list has been modified since last time we cached it, update the cache
- if ( $localFileRefs !== $this->getFileDependencies( $context ) ) {
+ if ( $localPaths !== $storedPaths ) {
$vary = $context->getSkin() . '|' . $context->getLanguage();
$cache = ObjectCache::getLocalClusterInstance();
$key = $cache->makeKey( __METHOD__, $this->getName(), $vary );
return; // T124649; avoid write slams
}
- // Use relative paths to avoid ghost entries when $IP changes (T111481)
- $deps = FormatJson::encode( self::getRelativePaths( $localFileRefs ) );
+ $deps = FormatJson::encode( $localPaths );
$dbw = wfGetDB( DB_MASTER );
$dbw->upsert( 'module_deps',
[
&& substr( rtrim( $scripts ), -1 ) !== ';'
) {
// Append semicolon to prevent weird bugs caused by files not
- // terminating their statements right (bug 27054)
+ // terminating their statements right (T29054)
$scripts .= ";\n";
}
}
if ( $context->shouldIncludeStyles() ) {
$styles = [];
// Don't create empty stylesheets like [ '' => '' ] for modules
- // that don't *have* any stylesheets (bug 38024).
+ // that don't *have* any stylesheets (T40024).
$stylePairs = $this->getStyles( $context );
if ( count( $stylePairs ) ) {
// If we are in debug mode without &only= set, we'll want to return an array of URLs
return true;
}
- // Bug 68488: For other modules (i.e. ones that are called in cached html output) only check
+ // T70488: For other modules (i.e. ones that are called in cached html output) only check
// page existance. This ensures that, if some pages in a module are temporarily blanked,
// we don't end omit the module's script or link tag on some pages.
return count( $revisions ) === 0;
}
}
+ if ( !$wikiModules ) {
+ // Nothing to preload
+ return;
+ }
+
$pageNames = array_keys( $allPages );
sort( $pageNames );
$hash = sha1( implode( '|', $pageNames ) );
// Ensure that the username isn't longer than 235 bytes, so that
// (at least for the builtin skins) user javascript and css files
- // will work. (bug 23080)
+ // will work. (T25080)
if ( strlen( $name ) > 235 ) {
wfDebugLog( 'username', __METHOD__ .
": '$name' invalid due to length" );
}
// Clean up name according to title rules,
- // but only when validation is requested (bug 12654)
+ // but only when validation is requested (T14654)
$t = ( $validate !== false ) ?
Title::newFromText( $name, NS_USER ) : Title::makeTitle( NS_USER, $name );
// Check for invalid titles
}
}
- // (bug 23343) Apply IP blocks to the contents of XFF headers, if enabled
+ // (T25343) Apply IP blocks to the contents of XFF headers, if enabled
if ( !$block instanceof Block
&& $wgApplyIpBlocksToXff
&& $ip !== null
$found = false;
// @todo FIXME: IPv6 ??? (https://bugs.php.net/bug.php?id=33170)
if ( IP::isIPv4( $ip ) ) {
- // Reverse IP, bug 21255
+ // Reverse IP, T23255
$ipReversed = implode( '.', array_reverse( explode( '.', $ip ) ) );
foreach ( (array)$bases as $base ) {
*/
public function isPingLimitable() {
global $wgRateLimitsExcludedIPs;
- if ( in_array( $this->getRequest()->getIP(), $wgRateLimitsExcludedIPs ) ) {
+ if ( IP::isInRanges( $this->getRequest()->getIP(), $wgRateLimitsExcludedIPs ) ) {
// No other good way currently to disable rate limits
// for specific IPs. :P
// But this is a crappy hack and should die.
// user_talk page; it's cleared one page view later in WikiPage::doViewUpdates().
}
+ /**
+ * Compute experienced level based on edit count and registration date.
+ *
+ * @return string 'newcomer', 'learner', or 'experienced'
+ */
+ public function getExperienceLevel() {
+ global $wgLearnerEdits,
+ $wgExperiencedUserEdits,
+ $wgLearnerMemberSince,
+ $wgExperiencedUserMemberSince;
+
+ if ( $this->isAnon() ) {
+ return false;
+ }
+
+ $editCount = $this->getEditCount();
+ $registration = $this->getRegistration();
+ $now = time();
+ $learnerRegistration = wfTimestamp( TS_MW, $now - $wgLearnerMemberSince * 86400 );
+ $experiencedRegistration = wfTimestamp( TS_MW, $now - $wgExperiencedUserMemberSince * 86400 );
+
+ if (
+ $editCount < $wgLearnerEdits ||
+ $registration > $learnerRegistration
+ ) {
+ return 'newcomer';
+ } elseif (
+ $editCount > $wgExperiencedUserEdits &&
+ $registration <= $experiencedRegistration
+ ) {
+ return 'experienced';
+ } else {
+ return 'learner';
+ }
+ }
+
/**
* Set a cookie on the user's client. Wrapper for
* WebResponse::setCookie
* }
* // do something with $user...
*
- * However, this was vulnerable to a race condition (bug 16020). By
+ * However, this was vulnerable to a race condition (T18020). By
* initialising the user object if the user exists, we aim to support this
* calling sequence as far as possible.
*
return $this->mBlock;
}
- # bug 13611: if the IP address the user is trying to create an account from is
+ # T15611: if the IP address the user is trying to create an account from is
# blocked with createaccount disabled, prevent new account creation there even
# when the user is logged in
if ( $this->mBlockedFromCreateAccount === false && !$this->isAllowed( 'ipblock-exempt' ) ) {
* @note Since these URLs get dropped directly into emails, using the
* short English names avoids insanely long URL-encoded links, which
* also sometimes can get corrupted in some browsers/mailers
- * (bug 6957 with Gmail and Internet Explorer).
+ * (T8957 with Gmail and Internet Explorer).
*
* @param string $page Special page
* @param string $token Token
# Note that the pattern requirement will always be satisfied if the
# input is empty, so we need required in all cases.
- # @todo FIXME: Bug 23769: This needs to not claim the password is required
+ # @todo FIXME: T25769: This needs to not claim the password is required
# if e-mail confirmation is being used. Since HTML5 input validation
# is b0rked anyway in some browsers, just return nothing. When it's
# re-enabled, fix this code to not output required for e-mail