mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Echo
synced 2024-11-27 17:20:40 +00:00
Track what wikis a user has unread notifications on
This implements a backend layer and database storage for tracking what wikis a user has unread notifications on. It is not yet exposed via any API. Whenever the notification counts on the local wiki are reset, a deferred update is queued to also update the central database table. Change-Id: Id1498bdeb5811d6848dc66781ffca03e726eab90
This commit is contained in:
parent
c3787f4c51
commit
b85f978ddd
8
Echo.php
8
Echo.php
|
@ -135,6 +135,14 @@ $wgNotificationReplyName = 'No Reply';
|
|||
// use any key defined in $wgExternalServers
|
||||
$wgEchoCluster = false;
|
||||
|
||||
// Shared database to use for keeping track of cross-wiki unread notifications
|
||||
// false to not keep track of it at all
|
||||
$wgEchoSharedTrackingDB = false;
|
||||
|
||||
// Cluster the shared tracking database is located on, false if it is on the
|
||||
// main one. Must be a key defined in $wgExternalServers
|
||||
$wgEchoSharedTrackingCluster = false;
|
||||
|
||||
// The max notification count showed in badge
|
||||
// The max number showed in bundled message, eg, <user> and 99+ others <action>
|
||||
$wgEchoMaxNotificationCount = 99;
|
||||
|
|
|
@ -86,6 +86,7 @@ $wgAutoloadClasses += array(
|
|||
'EchoTextEmailFormatter' => __DIR__ . '/includes/EmailFormatter.php',
|
||||
'EchoTitleLocalCache' => __DIR__ . '/includes/cache/TitleLocalCache.php',
|
||||
'EchoTitleLocalCacheTest' => __DIR__ . '/tests/phpunit/cache/TitleLocalCacheTest.php',
|
||||
'EchoUnreadWikis' => __DIR__ . '/includes/UnreadWikis.php',
|
||||
'EchoUserLocator' => __DIR__ . '/includes/UserLocator.php',
|
||||
'EchoUserLocatorTest' => __DIR__ . '/tests/phpunit/UserLocatorTest.php',
|
||||
'EchoUserNotificationGateway' => __DIR__ . '/includes/gateway/UserNotificationGateway.php',
|
||||
|
|
18
db_patches/echo_unread_wikis.sql
Normal file
18
db_patches/echo_unread_wikis.sql
Normal file
|
@ -0,0 +1,18 @@
|
|||
CREATE TABLE /*_*/echo_unread_wikis (
|
||||
# Primary key
|
||||
euw_id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
# Global user id
|
||||
euw_user INT UNSIGNED NOT NULL,
|
||||
# Name of wiki
|
||||
euw_wiki VARCHAR(30) NOT NULL,
|
||||
# unread alerts count on that wiki
|
||||
euw_alerts INT UNSIGNED NOT NULL,
|
||||
# Timestamp of the most recent unread alert
|
||||
euw_alerts_ts BINARY(14) NOT NULL,
|
||||
# unread messages count on that wiki
|
||||
euw_messages INT UNSIGNED NOT NULL,
|
||||
# Timestamp of the most recent unread message
|
||||
euw_messages_ts BINARY(14) NOT NULL
|
||||
) /*$wgDBTableOptions*/;
|
||||
|
||||
CREATE UNIQUE INDEX /*i*/echo_unread_wikis_user_wiki ON /*_*/echo_unread_wikis (euw_user,euw_wiki);
|
|
@ -6,25 +6,25 @@
|
|||
*/
|
||||
class MWEchoDbFactory {
|
||||
|
||||
/**
|
||||
* The wiki to access the database for
|
||||
* @var string|bool
|
||||
*/
|
||||
protected $wiki;
|
||||
|
||||
/**
|
||||
* The cluster for the database
|
||||
* @var string|bool
|
||||
*/
|
||||
protected $cluster;
|
||||
private $cluster;
|
||||
|
||||
private $shared;
|
||||
|
||||
private $sharedCluster;
|
||||
|
||||
/**
|
||||
* @param string|bool
|
||||
* @param string|bool
|
||||
* @param string|bool $cluster
|
||||
* @param string|bool $shared
|
||||
* @param string|bool $sharedCluster
|
||||
*/
|
||||
public function __construct( $cluster = false, $wiki = false ) {
|
||||
public function __construct( $cluster = false, $shared = false, $sharedCluster = false ) {
|
||||
$this->cluster = $cluster;
|
||||
$this->wiki = $wiki;
|
||||
$this->shared = $shared;
|
||||
$this->sharedCluster = $sharedCluster;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,22 +35,34 @@ class MWEchoDbFactory {
|
|||
* @return MWEchoDbFactory
|
||||
*/
|
||||
public static function newFromDefault() {
|
||||
global $wgEchoCluster;
|
||||
global $wgEchoCluster, $wgEchoSharedTrackingDB, $wgEchoSharedTrackingCluster;
|
||||
|
||||
return new self( $wgEchoCluster );
|
||||
return new self( $wgEchoCluster, $wgEchoSharedTrackingDB, $wgEchoSharedTrackingCluster );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database load balancer
|
||||
* @param $wiki string|bool The wiki ID, or false for the current wiki
|
||||
* @return LoadBalancer
|
||||
*/
|
||||
protected function getLB() {
|
||||
// Use the external db defined for Echo
|
||||
if ( $this->cluster ) {
|
||||
$lb = wfGetLBFactory()->getExternalLB( $this->cluster, $this->wiki );
|
||||
$lb = wfGetLBFactory()->getExternalLB( $this->cluster );
|
||||
} else {
|
||||
$lb = wfGetLB( $this->wiki );
|
||||
$lb = wfGetLB();
|
||||
}
|
||||
|
||||
return $lb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LoadBalancer
|
||||
*/
|
||||
protected function getSharedLB() {
|
||||
if ( $this->sharedCluster ) {
|
||||
$lb = wfGetLBFactory()->getExternalLB( $this->sharedCluster );
|
||||
} else {
|
||||
$lb = wfGetLB();
|
||||
}
|
||||
|
||||
return $lb;
|
||||
|
@ -63,9 +75,24 @@ class MWEchoDbFactory {
|
|||
* @return DatabaseBase
|
||||
*/
|
||||
public function getEchoDb( $db, $groups = array() ) {
|
||||
return $this->getLB()->getConnection( $db, $groups, $this->wiki );
|
||||
return $this->getLB()->getConnection( $db, $groups );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $db int Index of the connection to get
|
||||
* @param array $groups Query groups
|
||||
* @return bool|DatabaseBase false if no shared db is configured
|
||||
*/
|
||||
public function getSharedDb( $db, $groups = array() ) {
|
||||
if ( !$this->shared ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->getSharedLB()->getConnection( $db, $groups, $this->shared );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper function for wfGetDB, some extensions like MobileFrontend is
|
||||
* using this to issue sql queries against Echo database directly. This
|
||||
|
|
|
@ -412,14 +412,22 @@ class MWEchoNotifUser {
|
|||
public function resetNotificationCount( $dbSource = DB_SLAVE ) {
|
||||
// Reset notification count for all sections as well
|
||||
$this->getNotificationCount( false, $dbSource, EchoAttributeManager::ALL );
|
||||
$this->getNotificationCount( false, $dbSource, EchoAttributeManager::ALERT );
|
||||
$this->getNotificationCount( false, $dbSource, EchoAttributeManager::MESSAGE );
|
||||
$alertCount = $this->getNotificationCount( false, $dbSource, EchoAttributeManager::ALERT );
|
||||
$msgCount = $this->getNotificationCount( false, $dbSource, EchoAttributeManager::MESSAGE );
|
||||
|
||||
$user = $this->mUser;
|
||||
// when notification count needs to be updated, last notification may have
|
||||
// changed too, so we need to invalidate that cache too
|
||||
$this->getLastUnreadNotificationTime( false, $dbSource, EchoAttributeManager::ALL );
|
||||
$this->getLastUnreadNotificationTime( false, $dbSource, EchoAttributeManager::ALERT );
|
||||
$this->getLastUnreadNotificationTime( false, $dbSource, EchoAttributeManager::MESSAGE );
|
||||
$alertUnread = $this->getLastUnreadNotificationTime( false, $dbSource, EchoAttributeManager::ALERT );
|
||||
$msgUnread = $this->getLastUnreadNotificationTime( false, $dbSource, EchoAttributeManager::MESSAGE );
|
||||
$this->mUser->invalidateCache();
|
||||
DeferredUpdates::addCallableUpdate( function () use ( $user, $alertCount, $alertUnread, $msgCount, $msgUnread ) {
|
||||
$uw = EchoUnreadWikis::newFromUser( $user );
|
||||
if ( $uw ) {
|
||||
$uw->updateCount( wfWikiID(), $alertCount, $alertUnread, $msgCount, $msgUnread );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
145
includes/UnreadWikis.php
Normal file
145
includes/UnreadWikis.php
Normal file
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Manages what wikis a user has unread notifications on
|
||||
*
|
||||
*/
|
||||
class EchoUnreadWikis {
|
||||
|
||||
const ALERT = 'alert';
|
||||
const MESSAGE = 'message';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var MWEchoDbFactory
|
||||
*/
|
||||
private $dbFactory;
|
||||
|
||||
/**
|
||||
* @param int $id Central user id
|
||||
*/
|
||||
public function __construct( $id ) {
|
||||
$this->id = $id;
|
||||
$this->dbFactory = MWEchoDbFactory::newFromDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* If CentralAuth is installed, use that. Otherwise
|
||||
* assume they're using shared user tables.
|
||||
*
|
||||
* @param User $user
|
||||
* @return EchoUnreadWikis|bool
|
||||
*/
|
||||
public static function newFromUser( User $user ) {
|
||||
if ( class_exists( 'CentralAuth' ) ) {
|
||||
// @todo don't be CA specific (see T111302/CentralIdLookup)
|
||||
$caUser = CentralAuthUser::getInstance( $user );
|
||||
if ( $caUser->isAttached() ) {
|
||||
return new self( $caUser->getId() );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return new self( $user->getId() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $index DB_* constant
|
||||
* @return bool|DatabaseBase
|
||||
*/
|
||||
private function getDB( $index ) {
|
||||
return $this->dbFactory->getSharedDb( $index );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getUnreadCounts() {
|
||||
$dbr = $this->getDB( DB_SLAVE );
|
||||
if ( $dbr === false ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$rows = $dbr->select(
|
||||
'echo_unread_wikis',
|
||||
array(
|
||||
'euw_wiki',
|
||||
'euw_alerts', 'euw_alerts_ts',
|
||||
'euw_messages', 'euw_messages_ts',
|
||||
),
|
||||
array( 'euw_user' => $this->id ),
|
||||
__METHOD__
|
||||
);
|
||||
|
||||
$wikis = array();
|
||||
foreach ( $rows as $row ) {
|
||||
if ( !$row->euw_alerts && !$row->euw_messages ) {
|
||||
// This shouldn't happen, but lets be safe...
|
||||
continue;
|
||||
}
|
||||
$wikis[$row->euw_wiki] = array(
|
||||
self::ALERT => array(
|
||||
'count' => $row->euw_alerts,
|
||||
'ts' => $row->euw_alerts_ts,
|
||||
),
|
||||
self::MESSAGE => array(
|
||||
'count' => $row->euw_messages,
|
||||
'ts' => $row->euw_messages_ts,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return $wikis;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $wiki
|
||||
* @param int $alertCount
|
||||
* @param MWTimestamp|bool $alertTime
|
||||
* @param int $msgCount
|
||||
* @param MWTimestamp|bool $msgTime
|
||||
*/
|
||||
public function updateCount( $wiki, $alertCount, $alertTime, $msgCount, $msgTime ) {
|
||||
$dbw = $this->getDB( DB_MASTER );
|
||||
if ( $dbw === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$defaultTS = '00000000000000';
|
||||
|
||||
if ( $alertCount || $msgCount ) {
|
||||
$values = array(
|
||||
'euw_alerts' => $alertCount,
|
||||
'euw_alerts_ts' => $alertCount
|
||||
? $alertTime->getTimestamp( TS_MW )
|
||||
: $defaultTS,
|
||||
'euw_messages' => $msgCount,
|
||||
'euw_messages_ts' => $msgCount
|
||||
? $msgTime->getTimestamp( TS_MW )
|
||||
: $defaultTS,
|
||||
);
|
||||
$dbw->upsert(
|
||||
'echo_unread_wikis',
|
||||
array(
|
||||
'euw_user' => $this->id,
|
||||
'euw_wiki' => $wiki,
|
||||
) + $values,
|
||||
array( 'euw_user', 'euw_wiki' ),
|
||||
$values,
|
||||
__METHOD__
|
||||
);
|
||||
} else {
|
||||
// No unread notifications, delete the row
|
||||
$dbw->delete(
|
||||
'echo_unread_wikis',
|
||||
array( 'euw_user' => $this->id, 'euw_wiki' => $wiki ),
|
||||
__METHOD__
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue