Bsitu has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/65349


Change subject: (bug 47912) Visting talk page should mark talk notif as read
......................................................................

(bug 47912) Visting talk page should mark talk notif as read

This needs some more manual testing and adding unit testing

Change-Id: Iadfe3cf7927d5318f89ba17f067000f9399060af
---
M Echo.php
M Hooks.php
M Notifier.php
M api/ApiEchoNotifications.php
M controller/NotificationController.php
M includes/DbEchoBackend.php
M includes/EchoBackend.php
A includes/NotifUser.php
M model/Notification.php
M special/SpecialNotifications.php
A tests/NotifUserTest.php
11 files changed, 339 insertions(+), 104 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Echo 
refs/changes/49/65349/1

diff --git a/Echo.php b/Echo.php
index e95d103..8b7512a 100644
--- a/Echo.php
+++ b/Echo.php
@@ -85,6 +85,7 @@
 $wgAutoloadClasses['MWEchoBackend'] = $dir . 'includes/EchoBackend.php';
 $wgAutoloadClasses['MWDbEchoBackend'] = $dir . 'includes/DbEchoBackend.php';
 $wgAutoloadClasses['MWEchoDbFactory'] = $dir . 'includes/EchoDbFactory.php';
+$wgAutoloadClasses['MWEchoNotifUser'] = $dir . 'includes/NotifUser.php';
 
 // Whitelist and Blacklist
 $wgAutoloadClasses['EchoContainmentList'] = $dir . 
'includes/ContainmentSet.php';
@@ -104,6 +105,7 @@
 $wgHooks['UserRights'][] = 'EchoHooks::onUserRights';
 $wgHooks['UserLoadOptions'][] = 'EchoHooks::onUserLoadOptions';
 $wgHooks['UserSaveOptions'][] = 'EchoHooks::onUserSaveOptions';
+$wgHooks['UserClearNewTalkNotification'][] = 
'EchoHooks::onUserClearNewTalkNotification';
 
 // Extension initialization
 $wgExtensionFunctions[] = 'EchoHooks::initEchoExtension';
@@ -198,6 +200,7 @@
 $wgHooks['EchoGetNotificationTypes'][] = 'EchoHooks::getNotificationTypes';
 $wgHooks['EchoGetBundleRules'][] = 'EchoHooks::onEchoGetBundleRules';
 $wgHooks['EchoAbortEmailNotification'][] = 
'EchoHooks::onEchoAbortEmailNotification';
+$wgHooks['EchoCreateNotificationComplete'][] = 
'EchoHooks::onEchoCreateNotificationComplete';
 
 // Hook appropriate events
 $wgHooks['ArticleSaveComplete'][] = 'EchoHooks::onArticleSaved';
diff --git a/Hooks.php b/Hooks.php
index 01e2ebe..286b64b 100644
--- a/Hooks.php
+++ b/Hooks.php
@@ -626,7 +626,7 @@
                        return true;
                }
 
-               $notificationCount = 
EchoNotificationController::getNotificationCount( $wgUser );
+               $notificationCount = MWEchoNotifUser::newFromUser( $wgUser 
)->getNotificationCount();
                $text = EchoNotificationController::formatNotificationCount( 
$notificationCount );
                $url = SpecialPage::getTitleFor( 'Notifications' 
)->getLocalURL();
                if ( $notificationCount == 0 ) {
@@ -678,7 +678,7 @@
                $timestamp = new MWTimestamp( wfTimestampNow() );
                if ( ! $user->isAnon() ) {
                        $vars['wgEchoOverlayConfiguration'] = array(
-                               'notification-count' => 
EchoNotificationController::getFormattedNotificationCount( $user ),
+                               'notification-count' => 
MWEchoNotifUser::newFromUser( $user )->getFormattedNotificationCount(),
                                'max-notification-count' => 
$wgEchoMaxNotificationCount,
                        );
                        $vars['wgEchoHelpPage'] = $wgEchoHelpPage;
@@ -769,7 +769,7 @@
                // Reset the notification count since it may have changed due 
to user
                // option changes. This covers both explicit changes in the 
preferences
                // and changes made through the options API (since both call 
this hook).
-               EchoNotificationController::resetNotificationCount( $user );
+               MWEchoNotifUser::newFromUser( $user )->resetNotificationCount();
                return true;
        }
 
@@ -835,4 +835,38 @@
 
                return true;
        }
+
+       /**
+        * Handler for UserClearNewTalkNotification hook.
+        * @see 
http://www.mediawiki.org/wiki/Manual:Hooks/UserClearNewTalkNotification
+        * @param $user User whose talk page notification should be marked as 
read
+        */
+       public static function onUserClearNewTalkNotification( User $user ) {
+               $notifUser = MWEchoNotifUser::newFromUser( $user );
+               if ( $notifUser ) {
+                       $notifUser->clearTalkNotification();
+               }
+               return true;
+       }
+
+       /**
+        * Handler for EchoCreateNotificationComplete hook.
+        * @param $notif EchoNotification
+        */
+       public static function onEchoCreateNotificationComplete( 
EchoNotification $notif ) {
+               if ( !$notif->getEvent() || !$notif->getUser() ) {
+                       return true;
+               }
+
+               $notifUser = MWEchoNotifUser::newFromUser( $notif->getUser() );
+               if ( !$notifUser ) {
+                       return true;
+               }
+
+               if ( $notif->getEvent()->getType() === 'edit-user-talk' ) {
+                       $notifUser->cacheTalkNotification( 
$notif->getEvent()->getId() );               
+               }
+
+               return true;
+       }
 }
diff --git a/Notifier.php b/Notifier.php
index c6b45d7..2901bf5 100644
--- a/Notifier.php
+++ b/Notifier.php
@@ -23,7 +23,7 @@
 
                MWEchoEventLogging::logSchemaEcho( $user, $event, 'web' );
 
-               EchoNotificationController::resetNotificationCount( $user, 
DB_MASTER );
+               MWEchoNotifUser::newFromUser( $user )->resetNotificationCount( 
DB_MASTER );
        }
 
        /**
diff --git a/api/ApiEchoNotifications.php b/api/ApiEchoNotifications.php
index de11101..5bc3308 100644
--- a/api/ApiEchoNotifications.php
+++ b/api/ApiEchoNotifications.php
@@ -11,15 +11,17 @@
                        $this->dieUsage( 'Login is required', 'login-required' 
);
                }
 
+               $notifUser = MWEchoNotifUser::newFromUser( $user );
+
                $params = $this->extractRequestParams();
 
                // There is no need to trigger markRead if all notifications 
are read
-               if ( EchoNotificationController::getNotificationCount( $user ) 
> 0 ) {
+               if ( $notifUser->getNotificationCount() > 0 ) {
                        if ( count( $params['markread'] ) ) {
                                // Make sure there is a limit to the update
-                               EchoNotificationController::markRead( $user, 
array_slice( $params['markread'], 0, ApiBase::LIMIT_SML2 ) );
+                               $notifUser->markRead( array_slice( 
$params['markread'], 0, ApiBase::LIMIT_SML2 ) );
                        } elseif ( $params['markallread'] ) {
-                               EchoNotificationController::markAllRead( $user 
);
+                               $notifUser->markAllRead();
                        }
                }
 
@@ -40,7 +42,7 @@
                }
 
                if ( in_array( 'count', $prop ) ) {
-                       $result['count'] = 
EchoNotificationController::getFormattedNotificationCount( $user );
+                       $result['count'] = 
$notifUser->getFormattedNotificationCount();
                }
 
                if ( in_array( 'index', $prop ) ) {
diff --git a/controller/NotificationController.php 
b/controller/NotificationController.php
index d15aca2..1809691 100644
--- a/controller/NotificationController.php
+++ b/controller/NotificationController.php
@@ -5,35 +5,6 @@
        static protected $userWhitelist;
 
        /**
-        * Retrieves number of unread notifications that a user has, would 
return
-        * $wgEchoMaxNotificationCount + 1 at most
-        *
-        * @param $user User object to check notifications for
-        * @param $cached bool Set to false to bypass the cache.
-        * @param $dbSource string use master or slave database to pull count
-        * @return Integer: Number of unread notifications.
-        */
-       public static function getNotificationCount( $user, $cached = true, 
$dbSource = DB_SLAVE ) {
-               global $wgMemc, $wgEchoBackend, $wgEchoConfig;
-
-               if ( $user->isAnon() ) {
-                       return 0;
-               }
-
-               $memcKey = wfMemcKey( 'echo-notification-count', 
$user->getId(), $wgEchoConfig['version'] );
-
-               if ( $cached && $wgMemc->get( $memcKey ) !== false ) {
-                       return intval( $wgMemc->get( $memcKey ) );
-               }
-
-               $count = $wgEchoBackend->getNotificationCount( $user, $dbSource 
);
-
-               $wgMemc->set( $memcKey, $count, 86400 );
-
-               return intval( $count );
-       }
-
-       /**
         * Get the enabled events for a user, which excludes user-dismissed 
events
         * from the general enabled events
         * @param $user User
@@ -136,20 +107,6 @@
        }
 
        /**
-        * Retrieves formatted number of unread notifications that a user has.
-        *
-        * @param $user User object to check notifications for
-        * @param $cached bool Set to false to bypass the cache.
-        * @param $dbSource string use master or slave database to pull count
-        * @return String: Number of unread notifications.
-        */
-       public static function getFormattedNotificationCount( $user, $cached = 
true, $dbSource = DB_SLAVE ) {
-               return self::formatNotificationCount(
-                               self::getNotificationCount( $user, $cached, 
$dbSource )
-                       );
-       }
-
-       /**
         * Format the notification count with Language::formatNum().  In 
addition, for large count,
         * return abbreviated version, e.g. 99+
         * @param $count int
@@ -168,57 +125,6 @@
                }
 
                return $count;
-       }
-
-       /**
-        * Mark one or more notifications read for a user.
-        *
-        * @param $user User object to mark items read for.
-        * @param $eventIDs Array of event IDs to mark read
-        */
-       public static function markRead( $user, $eventIDs ) {
-               global $wgEchoBackend;
-
-               $eventIDs = array_filter( (array)$eventIDs, 'is_numeric' );
-               if ( !$eventIDs || wfReadOnly() ) {
-                       return;
-               }
-               $wgEchoBackend->markRead( $user, $eventIDs );
-               self::resetNotificationCount( $user, DB_MASTER );
-       }
-
-       /**
-        * @param $user User to mark all notifications read for
-        * @return boolean
-        */
-       public static function markAllRead( $user ) {
-               global $wgEchoBackend, $wgEchoMaxNotificationCount;
-
-               if ( wfReadOnly() ) {
-                       return false;
-               }
-
-               $notificationCount = self::getNotificationCount( $user );
-               // Only update all the unread notifications if it isn't a huge 
number.
-               // TODO: Implement batched jobs it's over the maximum.
-               if ( $notificationCount <= $wgEchoMaxNotificationCount ) {
-                       $wgEchoBackend->markAllRead( $user );
-                       self::resetNotificationCount( $user, DB_MASTER );
-                       return true;
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Recalculates the number of notifications that a user has.
-        *
-        * @param $user User object
-        * @param $dbSource string use master or slave database to pull count
-        */
-       public static function resetNotificationCount( $user, $dbSource = 
DB_SLAVE ) {
-               self::getNotificationCount( $user, false, $dbSource );
-               $user->invalidateCache();
        }
 
        /**
diff --git a/includes/DbEchoBackend.php b/includes/DbEchoBackend.php
index 00f5773..2e37c50 100644
--- a/includes/DbEchoBackend.php
+++ b/includes/DbEchoBackend.php
@@ -299,4 +299,32 @@
                return $db->numRows( $res );
        }
 
+       /**
+        * Get the unread notification event for a user, should only call this 
function
+        * if the number of unread notification is reasonable
+        * @param $user User
+        * @param $type string
+        */
+       public function getUnreadNotifications( $user, $type ) {
+               $dbr = MWEchoDbFactory::getDB( DB_SLAVE );
+               $res = $dbr->select(
+                       array( 'echo_notification', 'echo_event' ),
+                       array( 'notification_event' ),
+                       array(
+                               'notification_user' => $user->getId(),
+                               'notification_bundle_base' => 1,
+                               'notification_read_timestamp' => null,
+                               'event_type' => $type,
+                               'notification_event = event_id'
+                       ),
+                       __METHOD__
+               );
+
+               $eventIds = array();
+               foreach ( $res as $row ) {
+                       $eventIds[$row->notification_event] = 
$row->notification_event;
+               }
+
+               return $eventIds;
+       }
 }
diff --git a/includes/EchoBackend.php b/includes/EchoBackend.php
index e63e7fa..c5dc4b1 100644
--- a/includes/EchoBackend.php
+++ b/includes/EchoBackend.php
@@ -117,8 +117,16 @@
         * Retrieves number of unread notifications that a user has.
         * @param $user User object to check notifications for
         * @param $dbSource string use master or slave storage to pull count
-        * @return ResultWrapper|bool
+        * @return int
         */
        abstract public function getNotificationCount( $user, $dbSource );
 
+       /**
+        * Get the event ids for corresponding unread notifications for an
+        * event type, return empty array if the notifcation count is greater
+        * than max count
+        * @param $user User object to check notification for
+        * @param $type string event type
+        */
+       abstract public function getUnreadNotifications( $user, $type );
 }
diff --git a/includes/NotifUser.php b/includes/NotifUser.php
new file mode 100644
index 0000000..3187443
--- /dev/null
+++ b/includes/NotifUser.php
@@ -0,0 +1,230 @@
+<?php
+
+/**
+ * Entity that represents a notification target user
+ */
+class MWEchoNotifUser {
+
+       /**
+        * Notification target user
+        * @var User
+        */
+       private $mUser;
+
+       /**
+        * Cache object
+        * @var BagOStuff
+        */
+       private $cache;
+
+       /**
+        * Meta data for cache
+        * @var array
+        */
+       private $cacheMeta;
+
+       /**
+        * Constructor for initialization
+        */
+       private function __construct( User $user ) {
+               global $wgMemc, $wgEchoConfig;
+               $this->mUser = $user;
+               $this->cache = $wgMemc;
+
+               $this->cacheMeta = array(
+                       'talk-notification' => array(
+                               'key' => wfMemcKey( 'echo-edit-user-talk', 
'event-id', $this->mUser->getId(), $wgEchoConfig['version'] ),
+                               'expiration' => 60 * 60 * 24 * 10,
+                       ),
+                       'notification-count' => array(
+                               'key' => wfMemcKey( 'echo-notification-count', 
$this->mUser->getId(), $wgEchoConfig['version'] ),
+                               'expiration' => 60 * 60 * 24
+                       ),
+               );
+       }
+
+       /**
+        * Factory method
+        */
+       public static function newFromUser( User $user ) {
+               if ( $user->isAnon() ) {
+                       throw new MWException( 'User must login to view 
notification!' );
+               }
+               return new MWEchoNotifUser( $user );
+       }
+
+       /**
+        * Overwrite default setup
+        */
+       public function setValue( $key, $value ) {
+               $this->$key = $value;
+       }
+
+       /**
+        * Clear talk page notification when a user visits their talk page, 
this would
+        * mark the notification as read if the total notification count is 
less than 100,
+        * otherwise this would do nothing since talk page notification is 
bundled and
+        * decrementing 1 would not affect 99+
+        * @param $type string the notification type
+        * @param $conds array conditions for clearing a notification
+        */
+       public function clearTalkNotification() {
+               global $wgEchoBackend;
+
+               $key = $this->cacheMeta['talk-notification']['key'];
+               $eventIds = $this->cache->get( $key );
+
+               // Nothing in cache, try to query the storage if needed
+               if ( $eventIds === false ) {
+                       $eventIds = $this->getUnreadNotifications( 
'edit-user-talk' );
+               }
+
+               if ( $eventIds ) {
+                       $this->markRead( $eventIds );
+                       $this->cache->set( $key, array(), 
$this->cacheMeta['talk-notification']['expiration'] );
+               }
+       }
+
+       /**
+        * Cache talk page notification for this user
+        * @param $eventIds array|int
+        */
+       public function cacheTalkNotification( $eventIds ) {
+               if ( !is_array( $eventIds ) ) {
+                       $eventIds = array( $eventIds => $eventIds );
+               }
+               $key = $this->cacheMeta['talk-notification']['key'];
+
+               $existing = $this->cache->get( $key );
+
+               // talk page notification is bundleld and hence there should be 
only one
+               // unread notification, keep the cache with a max of 5 records 
to handle
+               // possible race conditions
+               if ( is_array( $existing ) ) {
+                       if ( count( $existing ) > 5 ) {
+                               array_shift( $existing );
+                       }
+               } else {
+                       $existing = $this->getUnreadNotifications( 
'edit-user-talk' );
+               }
+
+               if ( $existing ) {
+                       $eventIds = $existing + $eventIds;
+               }
+
+               $this->cache->set( $key, $eventIds, 
$this->cacheMeta['talk-notification']['expiration'] );
+       }
+
+       /**
+        * Get event ids for corresponding unread notifications for an event 
type,
+        * return empty array if the notification count is greater than max 
notification
+        * count
+        * @return array
+        */
+       protected function getUnreadNotifications( $type ) {
+               global $wgEchoBackend, $wgEchoMaxNotificationCount;
+
+               $count = $this->getNotificationCount();
+
+               $eventIds = array();
+
+               // if there are 99+ notifications, it is not necessary to 
decrement the count
+               if ( $count <= $wgEchoMaxNotificationCount ) {
+                       $eventIds =  $wgEchoBackend->getUnreadNotifications( 
$this->mUser, $type );
+               }
+
+               return $eventIds;
+       }
+
+       /**
+        * Retrieves number of unread notifications that a user has, would 
return
+        * $wgEchoMaxNotificationCount + 1 at most
+        *
+        * @param $cached bool Set to false to bypass the cache.
+        * @param $dbSource string use master or slave database to pull count
+        * @return Integer: Number of unread notifications.
+        */
+       public function getNotificationCount( $cached = true, $dbSource = 
DB_SLAVE ) {
+               global $wgEchoBackend, $wgEchoConfig;
+
+               if ( $this->mUser->isAnon() ) {
+                       return 0;
+               }
+
+               $memcKey = $this->cacheMeta['notification-count']['key'];
+
+               if ( $cached && $this->cache->get( $memcKey ) !== false ) {
+                       return (int)$this->cache->get( $memcKey );
+               }
+
+               $count = $wgEchoBackend->getNotificationCount( $this->mUser, 
$dbSource );
+
+               $this->cache->set( $memcKey, $count, 
$this->cacheMeta['notification-count']['expiration'] );
+
+               return (int)$count;
+       }
+
+       /**
+        * Mark one or more notifications read for a user.
+        *
+        * @param $eventIDs Array of event IDs to mark read
+        */
+       public function markRead( $eventIDs ) {
+               global $wgEchoBackend;
+
+               $eventIDs = array_filter( (array)$eventIDs, 'is_numeric' );
+               if ( !$eventIDs || wfReadOnly() ) {
+                       return;
+               }
+               $wgEchoBackend->markRead( $this->mUser, $eventIDs );
+               $this->resetNotificationCount( DB_MASTER );
+       }
+
+       /**
+        * Attempt to mark all notifications as read
+        *
+        * @return boolean
+        */
+       public function markAllRead() {
+               global $wgEchoBackend, $wgEchoMaxNotificationCount;
+
+               if ( wfReadOnly() ) {
+                       return false;
+               }
+
+               $notificationCount = $this->getNotificationCount();
+               // Only update all the unread notifications if it isn't a huge 
number.
+               // TODO: Implement batched jobs it's over the maximum.
+               if ( $notificationCount <= $wgEchoMaxNotificationCount ) {
+                       $wgEchoBackend->markAllRead( $this->mUser );
+                       $this->resetNotificationCount( DB_MASTER );
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Recalculates the number of notifications that a user has.
+        *
+        * @param $dbSource string use master or slave database to pull count
+        */
+       public function resetNotificationCount( $dbSource = DB_SLAVE ) {
+               $this->getNotificationCount( false, $dbSource );
+               $this->mUser->invalidateCache();
+       }
+
+       /**
+        * Retrieves formatted number of unread notifications that a user has.
+        *
+        * @param $cached bool Set to false to bypass the cache.
+        * @param $dbSource string use master or slave database to pull count
+        * @return String: Number of unread notifications.
+        */
+       public function getFormattedNotificationCount( $cached = true, 
$dbSource = DB_SLAVE ) {
+               return EchoNotificationController::formatNotificationCount(
+                               $this->getNotificationCount( $cached, $dbSource 
)
+                       );
+       }
+
+}
diff --git a/model/Notification.php b/model/Notification.php
index c32d6c0..d0a6abc 100644
--- a/model/Notification.php
+++ b/model/Notification.php
@@ -104,6 +104,8 @@
                }
 
                $wgEchoBackend->createNotification( $row );
+
+               wfRunHooks( 'EchoCreateNotificationComplete', array( $this ) );
        }
 
        /**
diff --git a/special/SpecialNotifications.php b/special/SpecialNotifications.php
index b7d6f66..e74c448 100644
--- a/special/SpecialNotifications.php
+++ b/special/SpecialNotifications.php
@@ -104,7 +104,7 @@
                $out->addExtensionStyle( 
"$wgExtensionAssetsPath/Echo/modules/base/ext.echo.base.css" );
                // Mark items as read
                if ( $unread ) {
-                       EchoNotificationController::markRead( $user, $unread );
+                       MWEchoNotifUser::newFromUser( $user )->markRead( 
$unread );
                }
        }
 
diff --git a/tests/NotifUserTest.php b/tests/NotifUserTest.php
new file mode 100644
index 0000000..498cbd7
--- /dev/null
+++ b/tests/NotifUserTest.php
@@ -0,0 +1,22 @@
+<?php
+
+class MWEchoNotifUserTest extends MediaWikiTestCase {
+
+       protected $notifUser;
+
+       protected function setUp() {
+               parent::setUp();
+               $this->setMwGlobals( 'wgMemc', new HashBagOStuff() );   
+       }
+
+       public function testNewFromUser() {
+               $this->notifUser = MWEchoNotifUser::newFromUser( 
User::newFromId( 2 ) );
+               $this->assertInstanceOf( 'MWEchoNotifUser', $this->notifUser );
+       }
+
+       /**
+        * @depends testNewFromUser
+        */
+       public function testClearTalkNotification() {
+               
+       }

-- 
To view, visit https://gerrit.wikimedia.org/r/65349
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Iadfe3cf7927d5318f89ba17f067000f9399060af
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Echo
Gerrit-Branch: master
Gerrit-Owner: Bsitu <[email protected]>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to