* @return int|null The corresponding user's ID, or null if user is nonexistent
*/
public static function idFromName( $name, $flags = self::READ_NORMAL ) {
+ // Don't explode on self::$idCacheByName[$name] if $name is not a string but e.g. a User object
+ $name = (string)$name;
$nt = Title::makeTitleSafe( NS_USER, $name );
if ( is_null( $nt ) ) {
// Illegal name
/**
* Get blocking information
- * @param bool $bFromSlave Whether to check the replica DB first.
+ * @param bool $bFromReplica Whether to check the replica DB first.
* To improve performance, non-critical checks are done against replica DBs.
* Check when actually saving should be done against master.
*/
- private function getBlockedStatus( $bFromSlave = true ) {
+ private function getBlockedStatus( $bFromReplica = true ) {
global $wgProxyWhitelist, $wgUser, $wgApplyIpBlocksToXff, $wgSoftBlockRanges;
if ( -1 != $this->mBlockedby ) {
}
// User/IP blocking
- $block = Block::newFromTarget( $this, $ip, !$bFromSlave );
+ $block = Block::newFromTarget( $this, $ip, !$bFromReplica );
// Cookie blocking
if ( !$block instanceof Block ) {
$xff = $this->getRequest()->getHeader( 'X-Forwarded-For' );
$xff = array_map( 'trim', explode( ',', $xff ) );
$xff = array_diff( $xff, [ $ip ] );
- $xffblocks = Block::getBlocksForIPList( $xff, $this->isAnon(), !$bFromSlave );
+ $xffblocks = Block::getBlocksForIPList( $xff, $this->isAnon(), !$bFromReplica );
$block = Block::chooseBlock( $xffblocks, $xff );
if ( $block instanceof Block ) {
# Mangle the reason to alert the user that the block
/**
* Check if user is blocked
*
- * @param bool $bFromSlave Whether to check the replica DB instead of
+ * @param bool $bFromReplica Whether to check the replica DB instead of
* the master. Hacked from false due to horrible probs on site.
* @return bool True if blocked, false otherwise
*/
- public function isBlocked( $bFromSlave = true ) {
- return $this->getBlock( $bFromSlave ) instanceof Block && $this->getBlock()->prevents( 'edit' );
+ public function isBlocked( $bFromReplica = true ) {
+ return $this->getBlock( $bFromReplica ) instanceof Block && $this->getBlock()->prevents( 'edit' );
}
/**
* Get the block affecting the user, or null if the user is not blocked
*
- * @param bool $bFromSlave Whether to check the replica DB instead of the master
+ * @param bool $bFromReplica Whether to check the replica DB instead of the master
* @return Block|null
*/
- public function getBlock( $bFromSlave = true ) {
- $this->getBlockedStatus( $bFromSlave );
+ public function getBlock( $bFromReplica = true ) {
+ $this->getBlockedStatus( $bFromReplica );
return $this->mBlock instanceof Block ? $this->mBlock : null;
}
* Check if user is blocked from editing a particular article
*
* @param Title $title Title to check
- * @param bool $fromSlave Whether to check the replica DB instead of the master
+ * @param bool $fromReplica Whether to check the replica DB instead of the master
* @return bool
*/
- public function isBlockedFrom( $title, $fromSlave = false ) {
+ public function isBlockedFrom( $title, $fromReplica = false ) {
$blocked = $this->isHidden();
if ( !$blocked ) {
- $block = $this->getBlock( $fromSlave );
+ $block = $this->getBlock( $fromReplica );
if ( $block ) {
$blocked = $block->preventsEdit( $title );
}
$this->isAnon() ? [ 'user_ip' => $this->getName() ] : [ 'user_id' => $this->getId() ],
__METHOD__ );
$rev = $timestamp ? Revision::loadFromTimestamp( $dbr, $utp, $timestamp ) : null;
- return [ [ 'wiki' => wfWikiID(), 'link' => $utp->getLocalURL(), 'rev' => $rev ] ];
+ return [
+ [
+ 'wiki' => WikiMap::getWikiIdFromDomain( WikiMap::getCurrentWikiDomain() ),
+ 'link' => $utp->getLocalURL(),
+ 'rev' => $rev
+ ]
+ ];
}
/**
// and it is always for the same wiki, but we double-check here in
// case that changes some time in the future.
if ( count( $newMessageLinks ) === 1
- && $newMessageLinks[0]['wiki'] === wfWikiID()
+ && WikiMap::isCurrentWikiId( $newMessageLinks[0]['wiki'] )
&& $newMessageLinks[0]['rev']
) {
/** @var Revision $newMessageRevision */
if ( $count === null ) {
// it has not been initialized. do so.
- $count = $this->initEditCount();
+ $count = $this->initEditCountInternal();
}
$this->mEditCount = $count;
}
}
$dbr = wfGetDB( DB_REPLICA );
$actorWhere = ActorMigration::newMigration()->getWhere( $dbr, 'rev_user', $this );
+ $tsField = isset( $actorWhere['tables']['temp_rev_user'] )
+ ? 'revactor_timestamp' : 'rev_timestamp';
$time = $dbr->selectField(
[ 'revision' ] + $actorWhere['tables'],
- 'rev_timestamp',
+ $tsField,
[ $actorWhere['conds'] ],
__METHOD__,
- [ 'ORDER BY' => 'rev_timestamp ASC' ],
+ [ 'ORDER BY' => "$tsField ASC" ],
$actorWhere['joins']
);
if ( !$time ) {
}
/**
- * Deferred version of incEditCountImmediate()
- *
- * This function, rather than incEditCountImmediate(), should be used for
- * most cases as it avoids potential deadlocks caused by concurrent editing.
+ * Schedule a deferred update to update the user's edit count
*/
public function incEditCount() {
- wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle(
- function () {
- $this->incEditCountImmediate();
- },
- __METHOD__
+ if ( $this->isAnon() ) {
+ return; // sanity
+ }
+
+ DeferredUpdates::addUpdate(
+ new UserEditCountUpdate( $this, 1 ),
+ DeferredUpdates::POSTSEND
);
}
/**
- * Increment the user's edit-count field.
- * Will have no effect for anonymous users.
- * @since 1.26
+ * This method should not be called outside User/UserEditCountUpdate
+ *
+ * @param int $count
*/
- public function incEditCountImmediate() {
- if ( $this->isAnon() ) {
- return;
- }
-
- $dbw = wfGetDB( DB_MASTER );
- // No rows will be "affected" if user_editcount is NULL
- $dbw->update(
- 'user',
- [ 'user_editcount=user_editcount+1' ],
- [ 'user_id' => $this->getId(), 'user_editcount IS NOT NULL' ],
- __METHOD__
- );
- // Lazy initialization check...
- if ( $dbw->affectedRows() == 0 ) {
- // Now here's a goddamn hack...
- $dbr = wfGetDB( DB_REPLICA );
- if ( $dbr !== $dbw ) {
- // If we actually have a replica DB server, the count is
- // at least one behind because the current transaction
- // has not been committed and replicated.
- $this->mEditCount = $this->initEditCount( 1 );
- } else {
- // But if DB_REPLICA is selecting the master, then the
- // count we just read includes the revision that was
- // just added in the working transaction.
- $this->mEditCount = $this->initEditCount();
- }
- } else {
- if ( $this->mEditCount === null ) {
- $this->getEditCount();
- $dbr = wfGetDB( DB_REPLICA );
- $this->mEditCount += ( $dbr !== $dbw ) ? 1 : 0;
- } else {
- $this->mEditCount++;
- }
- }
- // Edit count in user cache too
- $this->invalidateCache();
+ public function setEditCountInternal( $count ) {
+ $this->mEditCount = $count;
}
/**
* Initialize user_editcount from data out of the revision table
*
- * @param int $add Edits to add to the count from the revision table
+ * This method should not be called outside User/UserEditCountUpdate
+ *
* @return int Number of edits
*/
- protected function initEditCount( $add = 0 ) {
+ public function initEditCountInternal() {
// Pull from a replica DB to be less cruel to servers
// Accuracy isn't the point anyway here
$dbr = wfGetDB( DB_REPLICA );
[],
$actorWhere['joins']
);
- $count = $count + $add;
$dbw = wfGetDB( DB_MASTER );
$dbw->update(
'user',
[ 'user_editcount' => $count ],
- [ 'user_id' => $this->getId() ],
+ [
+ 'user_id' => $this->getId(),
+ 'user_editcount IS NULL OR user_editcount < ' . (int)$count
+ ],
__METHOD__
);