use DateTimeZone;
use Exception;
+ use WikiMap;
use MWDebug;
use MWExceptionHandler;
use Psr\Log\AbstractLogger;
* @return null
*/
public function log( $level, $message, array $context = [] ) {
+ global $wgDBerrorLog;
+
if ( is_string( $level ) ) {
$level = self::$levelMapping[$level];
}
- if ( $this->channel === 'DBQuery' && isset( $context['method'] )
- && isset( $context['master'] ) && isset( $context['runtime'] )
+ if ( $this->channel === 'DBQuery'
+ && isset( $context['method'] )
+ && isset( $context['master'] )
+ && isset( $context['runtime'] )
) {
- MWDebug::query( $message, $context['method'], $context['master'], $context['runtime'] );
- return; // only send profiling data to MWDebug profiling
+ // Also give the query information to the MWDebug tools
+ $enabled = MWDebug::query(
+ $message,
+ $context['method'],
+ $context['master'],
+ $context['runtime']
+ );
+ if ( $enabled ) {
+ // If we the toolbar was enabled, return early so that we don't
+ // also log the query to the main debug output.
+ return;
+ }
}
+ // If this is a DB-related error, and the site has $wgDBerrorLog
+ // configured, rewrite the channel as wfLogDBError instead.
+ // Likewise, if the site does not use $wgDBerrorLog, it should
+ // configurable like any other channel via $wgDebugLogGroups
+ // or $wgMWLoggerDefaultSpi.
if ( isset( self::$dbChannels[$this->channel] )
&& $level >= self::$levelMapping[LogLevel::ERROR]
+ && $wgDBerrorLog
) {
// Format and write DB errors to the legacy locations
$effectiveChannel = 'wfLogDBError';
$date = $d->format( 'D M j G:i:s T Y' );
$host = wfHostname();
- $wiki = wfWikiID();
+ $wiki = WikiMap::getWikiIdFromDomain( WikiMap::getCurrentWikiDomain() );
$text = "{$date}\t{$host}\t{$wiki}\t{$message}\n";
return $text;
*/
protected static function formatAsWfDebugLog( $channel, $message, $context ) {
$time = wfTimestamp( TS_DB );
- $wiki = wfWikiID();
+ $wiki = WikiMap::getWikiIdFromDomain( WikiMap::getCurrentWikiDomain() );
$host = wfHostname();
$text = "{$time} {$host} {$wiki}: {$message}\n";
return $text;
*/
public static function getDefaultOption( $opt ) {
$defOpts = self::getDefaultOptions();
- if ( isset( $defOpts[$opt] ) ) {
- return $defOpts[$opt];
- } else {
- return null;
- }
+ return $defOpts[$opt] ?? null;
}
/**
* Check if user is blocked from editing a particular article
*
* @param Title $title Title to check
- * @param bool $bFromSlave Whether to check the replica DB instead of the master
+ * @param bool $fromSlave Whether to check the replica DB instead of the master
* @return bool
*/
- public function isBlockedFrom( $title, $bFromSlave = false ) {
- global $wgBlockAllowsUTEdit;
+ public function isBlockedFrom( $title, $fromSlave = false ) {
+ $blocked = $this->isHidden();
- $blocked = $this->isBlocked( $bFromSlave );
- $allowUsertalk = ( $wgBlockAllowsUTEdit ? $this->mAllowUsertalk : false );
- // If a user's name is suppressed, they cannot make edits anywhere
- if ( !$this->mHideName && $allowUsertalk && $title->getText() === $this->getName()
- && $title->getNamespace() == NS_USER_TALK ) {
- $blocked = false;
- wfDebug( __METHOD__ . ": self-talk page, ignoring any blocks\n" );
+ if ( !$blocked ) {
+ $block = $this->getBlock( $fromSlave );
+ if ( $block ) {
+ $blocked = $block->preventsEdit( $title );
+ }
}
+ // only for the purpose of the hook. We really don't need this here.
+ $allowUsertalk = $this->mAllowUsertalk;
+
Hooks::run( 'UserIsBlockedFrom', [ $this, $title, &$blocked, &$allowUsertalk ] );
return $blocked;
*/
public function isHidden() {
if ( $this->mHideName !== null ) {
- return $this->mHideName;
+ return (bool)$this->mHideName;
}
$this->getBlockedStatus();
if ( !$this->mHideName ) {
$this->mHideName = $authUser && $authUser->isHidden();
Hooks::run( 'UserIsHidden', [ $this, &$this->mHideName ] );
}
- return $this->mHideName;
+ return (bool)$this->mHideName;
}
/**
$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
+ ]
+ ];
}
/**
if ( $count === null ) {
// it has not been initialized. do so.
- $count = $this->initEditCount();
+ $count = $this->initEditCountInternal();
}
$this->mEditCount = $count;
}
return $this->mBlock && $this->mBlock->prevents( 'sendemail' );
}
+ /**
+ * Get whether the user is blocked from using Special:Upload
+ *
+ * @return bool
+ */
+ public function isBlockedFromUpload() {
+ $this->getBlockedStatus();
+ return $this->mBlock && $this->mBlock->prevents( 'upload' );
+ }
+
/**
* Get whether the user is allowed to create an account.
* @return bool
}
/**
- * 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__
);