proper, published library, which is now tagged as v1.0.0.
* A new message (defaulting to blank), 'editnotice-notext', can be shown to users
when they are editing if no edit notices apply to the page being edited.
+* (T94536) You can now make the sitenotice appear to logged-in users only by
+ editing MediaWiki:Anonnotice and replacing its content with "". Setting it to
+ "-" (default) will continue disable it and fallback to MediaWiki:Sitenotice.
==== External libraries ====
* MediaWiki now requires certain external libraries to be installed. In the past
$dbw = wfGetDB( DB_MASTER );
}
- # Don't collide with expired blocks
- Block::purgeExpired();
+ # Periodic purge via commit hooks
+ if ( mt_rand( 0, 9 ) == 0 ) {
+ Block::purgeExpired();
+ }
$row = $this->getDatabaseArray();
$row['ipb_id'] = $dbw->nextSequenceValue( "ipblocks_ipb_id_seq" );
- $dbw->insert(
- 'ipblocks',
- $row,
- __METHOD__,
- array( 'IGNORE' )
- );
+ $dbw->insert( 'ipblocks', $row, __METHOD__, array( 'IGNORE' ) );
$affected = $dbw->affectedRows();
+
+ # Don't collide with expired blocks.
+ # Do this after trying to insert to avoid pointless gap locks.
+ if ( !$affected ) {
+ $dbw->delete( 'ipblocks',
+ array(
+ 'ipb_address' => $row['ipb_address'],
+ 'ipb_user' => $row['ipb_user'],
+ 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() )
+ ),
+ __METHOD__
+ );
+
+ $dbw->insert( 'ipblocks', $row, __METHOD__, array( 'IGNORE' ) );
+ $affected = $dbw->affectedRows();
+ }
+
$this->mId = $dbw->insertId();
if ( $affected ) {
wfDebugLog( 'RedirectedPosts', "Redirected from HTTP to HTTPS: $oldUrl" );
}
// Setup dummy Title, otherwise OutputPage::redirect will fail
- $title = Title::newFromText( NS_MAIN, 'REDIR' );
+ $title = Title::newFromText( 'REDIR', NS_MAIN );
$this->context->setTitle( $title );
$output = $this->context->getOutput();
// Since we only do this redir to change proto, always send a vary header
global $wgAuth;
if ( wfReadOnly() ) {
- return; // @TODO: caller should deal with this instead!
+ // @TODO: caller should deal with this instead!
+ // This should really just be an exception.
+ MWExceptionHandler::logException( new DBExpectedError(
+ null,
+ "Could not update user with ID '{$this->mId}'; DB is read-only."
+ ) );
+ return;
}
$this->load();
unset( $params[$idsKey] );
}
if ( isset( $params[$ofieldKey] ) ) {
- $params[] = $params[$ofieldKey];
+ $params[] = 'ofield=' . $params[$ofieldKey];
unset( $params[$ofieldKey] );
}
if ( isset( $params[$nfieldKey] ) ) {
- $params[] = $params[$nfieldKey];
+ $params[] = 'nfield=' . $params[$nfieldKey];
unset( $params[$nfieldKey] );
}
}
$data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
} elseif ( $from === self::READ_NORMAL ) {
$data = $this->pageDataFromTitle( wfGetDB( DB_SLAVE ), $this->mTitle );
- // Use a "last rev inserted" timestamp key to diminish the issue of slave lag.
- // Note that DB also stores the master position in the session and checks it.
- $touched = $this->getCachedLastEditTime();
- if ( $touched ) { // key set
- if ( !$data || $touched > wfTimestamp( TS_MW, $data->page_touched ) ) {
- $from = self::READ_LATEST;
- $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
- }
+ if ( !$data
+ && wfGetLB()->getServerCount() > 1
+ && wfGetLB()->hasOrMadeRecentMasterChanges()
+ ) {
+ $from = self::READ_LATEST;
+ $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
}
} else {
// No idea from where the caller got this data, assume slave database.
}
}
- /**
- * Get the cached timestamp for the last time the page changed.
- * This is only used to help handle slave lag by comparing to page_touched.
- * @return string MW timestamp
- */
- protected function getCachedLastEditTime() {
- global $wgMemc;
- $key = wfMemcKey( 'page-lastedit', md5( $this->mTitle->getPrefixedDBkey() ) );
- return $wgMemc->get( $key );
- }
-
- /**
- * Set the cached timestamp for the last time the page changed.
- * This is only used to help handle slave lag by comparing to page_touched.
- * @param string $timestamp
- * @return void
- */
- public function setCachedLastEditTime( $timestamp ) {
- global $wgMemc;
- $key = wfMemcKey( 'page-lastedit', md5( $this->mTitle->getPrefixedDBkey() ) );
- $wgMemc->set( $key, wfTimestamp( TS_MW, $timestamp ), 60 * 15 );
- }
-
/**
* Determine whether a page would be suitable for being counted as an
* article in the site_stats table based on the title & its content
if ( $result ) {
$this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
$this->setLastEdit( $revision );
- $this->setCachedLastEditTime( $now );
$this->mLatest = $revision->getId();
$this->mIsRedirect = (bool)$rt;
// Update the LinkCache.
$baseRevId = null;
if ( $edittime && $sectionId !== 'new' ) {
- $dbw = wfGetDB( DB_MASTER );
- $rev = Revision::loadFromTimestamp( $dbw, $this->mTitle, $edittime );
+ $dbr = wfGetDB( DB_SLAVE );
+ $rev = Revision::loadFromTimestamp( $dbr, $this->mTitle, $edittime );
+ // Try the master if this thread may have just added it.
+ // This could be abstracted into a Revision method, but we don't want
+ // to encourage loading of revisions by timestamp.
+ if ( !$rev
+ && wfGetLB()->getServerCount() > 1
+ && wfGetLB()->hasOrMadeRecentMasterChanges()
+ ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $rev = Revision::loadFromTimestamp( $dbw, $this->mTitle, $edittime );
+ }
if ( $rev ) {
$baseRevId = $rev->getId();
}
if ( is_null( $baseRevId ) || $sectionId === 'new' ) {
$oldContent = $this->getContent();
} else {
- // TODO: try DB_SLAVE first
- $dbw = wfGetDB( DB_MASTER );
- $rev = Revision::loadFromId( $dbw, $baseRevId );
-
+ $rev = Revision::newFromId( $baseRevId );
if ( !$rev ) {
wfDebug( __METHOD__ . " asked for bogus section (page: " .
$this->getId() . "; section: $sectionId)\n" );
* Get a cached notice
*
* @param string $name Message name, or 'default' for $wgSiteNotice
- * @return string HTML fragment
+ * @return string|bool HTML fragment, or false to indicate that the caller
+ * should fall back to the next notice in its sequence
*/
private function getCachedNotice( $name ) {
global $wgRenderHashAppend, $parserMemc, $wgContLang;
}
} else {
$msg = $this->msg( $name )->inContentLanguage();
- if ( $msg->isDisabled() ) {
+ if ( $msg->isBlank() ) {
+ return '';
+ } elseif ( $msg->isDisabled() ) {
return false;
}
$notice = $msg->plain();
$siteNotice = $this->getCachedNotice( 'sitenotice' );
} else {
$anonNotice = $this->getCachedNotice( 'anonnotice' );
- if ( !$anonNotice ) {
+ if ( $anonNotice === false ) {
$siteNotice = $this->getCachedNotice( 'sitenotice' );
} else {
$siteNotice = $anonNotice;
}
}
- if ( !$siteNotice ) {
+ if ( $siteNotice === false ) {
$siteNotice = $this->getCachedNotice( 'default' );
}
}
$text = $msg->text();
} else {
global $wgContLang;
- $text = $wgContLang->getFormattedNsText(
+ $text = $wgContLang->getConverter()->convertNamespace(
MWNamespace::getSubject( $title->getNamespace() ) );
}
}
function showList() {
- # Purge expired entries on one in every 10 queries
- if ( !mt_rand( 0, 10 ) ) {
- Block::purgeExpired();
- }
-
$conds = array();
# Is the user allowed to see hidden blocks?
if ( !$this->getUser()->isAllowed( 'hideuser' ) ) {
'join_conds' => array( 'user' => array( 'LEFT JOIN', 'user_id = ipb_by' ) )
);
+ # Filter out any expired blocks
+ $db = $this->getDatabase();
+ $info['conds'][] = 'ipb_expiry > ' . $db->addQuotes( $db->timestamp() );
+
# Is the user allowed to see hidden blocks?
if ( !$this->getUser()->isAllowed( 'hideuser' ) ) {
$info['conds']['ipb_deleted'] = 0;
*/
protected function getWatchlistInfo() {
$titles = array();
- $dbr = wfGetDB( DB_MASTER );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select(
array( 'watchlist' ),
"emailccsubject": "Copy of your message to $1: $2",
"emailsent": "Email sent",
"emailsenttext": "Your email message has been sent.",
- "emailuserfooter": "This email was sent by $1 to $2 by the \"Email user\" function at {{SITENAME}}.",
+ "emailuserfooter": "This email was sent by $1 to $2 by the \"{{int:emailpage}}\" function at {{SITENAME}}.",
"usermessage-summary": "Leaving system message.",
"usermessage-editor": "System messenger",
"usermessage-template": "MediaWiki:UserMessage",
),
'dependencies' => array(
'mediawiki.api.edit',
+ 'mediawiki.messagePoster',
),
'targets' => array( 'desktop', 'mobile' ),
),
* by MessagePosters that require one, unless the message already contains the string
* ~~~.
* @return {jQuery.Promise} Promise completing when the post succeeds or fails.
- * @return {Function} return.done
- * @return {Function} return.fail
- * @return {string} return.fail.primaryError Primary error code. For a mw.Api failure,
- * this should be 'api-fail'.
- * @return {string} return.fail.secondaryError Secondary error code. For a mw.Api failure,
- * this, should be mw.Api's code, e.g. 'http', 'ok-but-empty', or the error passed through
- * from the server.
- * @return {Mixed} return.fail.details Further details about the error
+ * For failure, will be rejected with three arguments:
+ *
+ * - primaryError - Primary error code. For a mw.Api failure,
+ * this should be 'api-fail'.
+ * - secondaryError - Secondary error code. For a mw.Api failure,
+ * this, should be mw.Api's code, e.g. 'http', 'ok-but-empty', or the error passed through
+ * from the server.
+ * - details - Further details about the error
*
* @localdoc
* The base class currently does nothing, but could be used for shared analytics or
* API and ResourceLoader requests in the background.
*
* @param {mw.Title} title Title that will be posted to
- * @return {jQuery.Promise} Promise for the MessagePoster
- * @return {Function} return.done Called if MessagePoster is retrieved
- * @return {mw.messagePoster.MessagePoster} return.done.poster MessagePoster
- * @return {Function} return.fail Called if MessagePoster could not be constructed
- * @return {string} return.fail.errorCode String error code
+ * @return {jQuery.Promise} Promise resolving to a mw.messagePoster.MessagePoster.
+ * For failure, rejected with up to three arguments:
+ *
+ * - errorCode Error code string
+ * - error Error explanation
+ * - details Further error details
*/
MwMessagePosterFactory.prototype.create = function ( title ) {
var pageId, page, contentModel, moduleName,