return $notificationTimestamp;
}
+ /**
+ * @param User $user
+ * @param int $unreadLimit
+ *
+ * @return int|bool The number of unread notifications
+ * true if greater than or equal to $unreadLimit
+ */
+ public function countUnreadNotifications( User $user, $unreadLimit = null ) {
+ $queryOptions = [];
+ if ( $unreadLimit !== null ) {
+ $unreadLimit = (int)$unreadLimit;
+ $queryOptions['LIMIT'] = $unreadLimit;
+ }
+
+ $dbr = $this->loadBalancer->getConnection( DB_SLAVE, [ 'watchlist' ] );
+ $rowCount = $dbr->selectRowCount(
+ 'watchlist',
+ '1',
+ [
+ 'wl_user' => $user->getId(),
+ 'wl_notificationtimestamp IS NOT NULL',
+ ],
+ __METHOD__,
+ $queryOptions
+ );
+ $this->loadBalancer->reuseConnection( $dbr );
+
+ if ( !isset( $unreadLimit ) ) {
+ return $rowCount;
+ }
+
+ if ( $rowCount >= $unreadLimit ) {
+ return true;
+ }
+
+ return $rowCount;
+ }
+
/**
* Check if the given title already is watched by the user, and if so
* add a watch for the new title.
}
if ( isset( $this->prop['unreadcount'] ) ) {
- $dbr = $this->getQuery()->getNamedDB( 'watchlist', DB_SLAVE, 'watchlist' );
-
- $count = $dbr->selectRowCount(
- 'watchlist',
- '1',
- [
- 'wl_user' => $user->getId(),
- 'wl_notificationtimestamp IS NOT NULL',
- ],
- __METHOD__,
- [ 'LIMIT' => self::WL_UNREAD_LIMIT ]
+ $unreadNotifications = WatchedItemStore::getDefaultInstance()->countUnreadNotifications(
+ $user,
+ self::WL_UNREAD_LIMIT
);
- if ( $count >= self::WL_UNREAD_LIMIT ) {
+ if ( $unreadNotifications === true ) {
$vals['unreadcount'] = self::WL_UNREAD_LIMIT . '+';
} else {
- $vals['unreadcount'] = $count;
+ $vals['unreadcount'] = $unreadNotifications;
}
}
$store->addWatch( $user, $title );
$this->assertNull( $store->loadWatchedItem( $user, $title )->getNotificationTimestamp() );
$initialVisitingWatchers = $store->countVisitingWatchers( $title, '20150202020202' );
+ $initialUnreadNotifications = $store->countUnreadNotifications( $user );
$store->updateNotificationTimestamp( $otherUser, $title, '20150202010101' );
$this->assertEquals(
$initialVisitingWatchers - 1,
$store->countVisitingWatchers( $title, '20150202020202' )
);
+ $this->assertEquals(
+ $initialUnreadNotifications + 1,
+ $store->countUnreadNotifications( $user )
+ );
+ $this->assertSame(
+ true,
+ $store->countUnreadNotifications( $user, $initialUnreadNotifications + 1 )
+ );
$this->assertTrue( $store->resetNotificationTimestamp( $user, $title ) );
$this->assertNull( $store->getWatchedItem( $user, $title )->getNotificationTimestamp() );
$this->assertEquals( $expected, $store->countWatchersMultiple( $titleValues ) );
}
- public function provideMinimumWatchers() {
+ public function provideIntWithDbUnsafeVersion() {
return [
[ 50 ],
[ "50; DROP TABLE watchlist;\n--" ],
}
/**
- * @dataProvider provideMinimumWatchers
+ * @dataProvider provideIntWithDbUnsafeVersion
*/
public function testCountWatchersMultiple_withMinimumWatchers( $minWatchers ) {
$titleValues = [
$this->assertEquals( 7, $store->countVisitingWatchers( $titleValue, '111' ) );
}
+ public function testCountUnreadNotifications() {
+ $user = $this->getMockNonAnonUserWithId( 1 );
+
+ $mockDb = $this->getMockDb();
+ $mockDb->expects( $this->exactly( 1 ) )
+ ->method( 'selectRowCount' )
+ ->with(
+ 'watchlist',
+ '1',
+ [
+ "wl_notificationtimestamp IS NOT NULL",
+ 'wl_user' => 1,
+ ],
+ $this->isType( 'string' )
+ )
+ ->will( $this->returnValue( 9 ) );
+
+ $mockCache = $this->getMockCache();
+ $mockCache->expects( $this->never() )->method( 'set' );
+ $mockCache->expects( $this->never() )->method( 'get' );
+ $mockCache->expects( $this->never() )->method( 'delete' );
+
+ $store = new WatchedItemStore(
+ $this->getMockLoadBalancer( $mockDb ),
+ $mockCache
+ );
+
+ $this->assertEquals( 9, $store->countUnreadNotifications( $user ) );
+ }
+
+ /**
+ * @dataProvider provideIntWithDbUnsafeVersion
+ */
+ public function testCountUnreadNotifications_withUnreadLimit_overLimit( $limit ) {
+ $user = $this->getMockNonAnonUserWithId( 1 );
+
+ $mockDb = $this->getMockDb();
+ $mockDb->expects( $this->exactly( 1 ) )
+ ->method( 'selectRowCount' )
+ ->with(
+ 'watchlist',
+ '1',
+ [
+ "wl_notificationtimestamp IS NOT NULL",
+ 'wl_user' => 1,
+ ],
+ $this->isType( 'string' ),
+ [ 'LIMIT' => 50 ]
+ )
+ ->will( $this->returnValue( 50 ) );
+
+ $mockCache = $this->getMockCache();
+ $mockCache->expects( $this->never() )->method( 'set' );
+ $mockCache->expects( $this->never() )->method( 'get' );
+ $mockCache->expects( $this->never() )->method( 'delete' );
+
+ $store = new WatchedItemStore(
+ $this->getMockLoadBalancer( $mockDb ),
+ $mockCache
+ );
+
+ $this->assertSame(
+ true,
+ $store->countUnreadNotifications( $user, $limit )
+ );
+ }
+
+ /**
+ * @dataProvider provideIntWithDbUnsafeVersion
+ */
+ public function testCountUnreadNotifications_withUnreadLimit_underLimit( $limit ) {
+ $user = $this->getMockNonAnonUserWithId( 1 );
+
+ $mockDb = $this->getMockDb();
+ $mockDb->expects( $this->exactly( 1 ) )
+ ->method( 'selectRowCount' )
+ ->with(
+ 'watchlist',
+ '1',
+ [
+ "wl_notificationtimestamp IS NOT NULL",
+ 'wl_user' => 1,
+ ],
+ $this->isType( 'string' ),
+ [ 'LIMIT' => 50 ]
+ )
+ ->will( $this->returnValue( 9 ) );
+
+ $mockCache = $this->getMockCache();
+ $mockCache->expects( $this->never() )->method( 'set' );
+ $mockCache->expects( $this->never() )->method( 'get' );
+ $mockCache->expects( $this->never() )->method( 'delete' );
+
+ $store = new WatchedItemStore(
+ $this->getMockLoadBalancer( $mockDb ),
+ $mockCache
+ );
+
+ $this->assertEquals(
+ 9,
+ $store->countUnreadNotifications( $user, $limit )
+ );
+ }
+
public function testDuplicateEntry_nothingToDuplicate() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )