EBernhardson has uploaded a new change for review.

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

Change subject: Introduce AttributeManager class
......................................................................

Introduce AttributeManager class

This is a precursor to splitting notifications into
alert and message sections.

Change-Id: Ic685f7026ab9b41407b51317780bbfadd05bf9f1
---
M Echo.php
M Hooks.php
M Notifier.php
M api/ApiEchoMarkRead.php
M api/ApiEchoNotifications.php
M controller/NotificationController.php
A includes/AttributeManager.php
M includes/NotifUser.php
M includes/gateway/UserNotificationGateway.php
M includes/mapper/NotificationMapper.php
M model/Event.php
M special/SpecialNotifications.php
M tests/EmailFormatterTest.php
A tests/includes/AttributeManagerTest.php
M tests/includes/gateway/UserNotificationGatewayTest.php
M tests/includes/mapper/NotificationMapperTest.php
16 files changed, 577 insertions(+), 187 deletions(-)


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

diff --git a/Echo.php b/Echo.php
index 8fb2dad..32c4c01 100644
--- a/Echo.php
+++ b/Echo.php
@@ -56,6 +56,7 @@
 $wgAutoloadClasses['MWEchoEmailBundler'] = $dir . 'includes/EmailBundler.php';
 $wgAutoloadClasses['MWDbEchoEmailBundler'] = $dir . 
'includes/DbEmailBundler.php';
 $wgAutoloadClasses['MWEchoEventLogging'] = $dir . 'includes/EventLogging.php';
+$wgAutoloadClasses['EchoAttributeManager'] = $dir . 
'includes/AttributeManager.php';
 
 // Database mappers && gateways
 $wgAutoloadClasses['EchoEventMapper'] = $dir . 
'includes/mapper/EventMapper.php';
diff --git a/Hooks.php b/Hooks.php
index a4f703b..ae38e57 100644
--- a/Hooks.php
+++ b/Hooks.php
@@ -333,9 +333,10 @@
                        if ( isset( $categoryData['no-dismiss'] ) && in_array( 
'all' , $categoryData['no-dismiss'] ) ) {
                                continue;
                        }
+                       $attributeManager = 
EchoAttributeManager::newFromGlobalVars();
                        // See if user is eligible to recieve this notification 
(per user group restrictions)
-                       if ( 
EchoNotificationController::getCategoryEligibility( $user, $category ) ) {
-                               $categoriesAndPriorities[$category] = 
EchoNotificationController::getCategoryPriority( $category );
+                       if ( $attributeManager->getCategoryEligibility( $user, 
$category ) ) {
+                               $categoriesAndPriorities[$category] = 
$attributeManager->getCategoryPriority( $category );
                        }
                }
                asort( $categoriesAndPriorities );
@@ -732,9 +733,11 @@
                // If a user is watching his/her own talk page, do not send 
talk page watchlist
                // email notification if the user is receiving Echo talk page 
notification
                if ( $title->isTalkPage() && 
$targetUser->getTalkPage()->equals( $title ) ) {
+                       $attributeManager = 
EchoAttributeManager::newFromGlobalVars();
+                       $events = $attributeManager->getUserEnabledEvents( 
$targetUser, 'email' );
                        if (
                                !self::isEchoDisabled( $targetUser )
-                               && in_array( 'edit-user-talk', 
EchoNotificationController::getUserEnabledEvents( $targetUser, 'email' ) )
+                               && in_array( 'edit-user-talk', $events )
                        ) {
                                // Do not send watchlist email notification, 
the user will receive an Echo notification
                                return false;
diff --git a/Notifier.php b/Notifier.php
index bac18ee..d32727d 100644
--- a/Notifier.php
+++ b/Notifier.php
@@ -12,7 +12,8 @@
        public static function notifyWithNotification( $user, $event ) {
                // Only create the notification if the user wants to recieve 
that type
                // of notification and they are eligible to recieve it. See bug 
47664.
-               $userWebNotifications = 
EchoNotificationController::getUserEnabledEvents( $user, 'web' );
+               $attributeManager = EchoAttributeManager::newFromGlobalVars();
+               $userWebNotifications = 
$attributeManager->getUserEnabledEvents( $user, 'web' );
                if ( !in_array( $event->getType(), $userWebNotifications ) ) {
                        return;
                }
@@ -40,11 +41,13 @@
                        return false;
                }
 
+               $attributeManager = EchoAttributeManager::newFromGlobalVars();
+               $userEmailNotifications = 
$attributeManager->getUserEnabledEvents( $user, 'email' );
                // See if the user wants to receive emails for this category or 
the user is eligible to receive this email
-               if ( in_array( $event->getType(), 
EchoNotificationController::getUserEnabledEvents( $user, 'email' ) ) ) {
+               if ( in_array( $event->getType(), $userEmailNotifications ) ) {
                        global $wgEchoEnableEmailBatch, $wgEchoNotifications, 
$wgNotificationSender, $wgNotificationSenderName, $wgNotificationReplyName, 
$wgEchoBundleEmailInterval;
 
-                       $priority = 
EchoNotificationController::getNotificationPriority( $event->getType() );
+                       $priority = $attributeManager->getNotificationPriority( 
$event->getType() );
 
                        $bundleString = $bundleHash = '';
 
diff --git a/api/ApiEchoMarkRead.php b/api/ApiEchoMarkRead.php
index 5a2a387..cc1e941 100644
--- a/api/ApiEchoMarkRead.php
+++ b/api/ApiEchoMarkRead.php
@@ -25,11 +25,9 @@
                        }
                }
 
-               $rawCount = $notifUser->getNotificationCount();
-
                $result = array(
                        'result' => 'success',
-                       'rawcount' => $rawCount,
+                       'rawcount' => $notifUser->getNotificationCount(),
                        'count' => 
EchoNotificationController::formatNotificationCount( $rawCount ),
                );
                $this->getResult()->addValue( 'query', $this->getModuleName(), 
$result );
diff --git a/api/ApiEchoNotifications.php b/api/ApiEchoNotifications.php
index 9e85e86..dfbbce6 100644
--- a/api/ApiEchoNotifications.php
+++ b/api/ApiEchoNotifications.php
@@ -21,46 +21,191 @@
 
                $prop = $params['prop'];
 
+               $attributeManager = EchoAttributeManager::newFromGlobalVars();
                $result = array();
                if ( in_array( 'list', $prop ) ) {
-                       $result['list'] = array();
-                       $notifMapper = new EchoNotificationMapper( 
MWEchoDbFactory::newFromDefault() );
-                       $notifs = $notifMapper->fetchByUser( $user, 
$params['limit'] + 1, $params['continue'], 'web' );
-                       foreach ( $notifs as $notif ) {
-                               $result['list'][$notif->getEvent()->getID()] = 
EchoDataOutputFormatter::formatOutput( $notif, $params['format'], $user );
-                       }
-
-                       // check if there is more elements than we request
-                       if ( count( $result['list'] ) > $params['limit'] ) {
-                               $lastItem = array_pop( $result['list'] );
-                               $result['continue'] = 
$lastItem['timestamp']['utcunix'] . '|' . $lastItem['id'];
-                       } else {
-                               $result['continue'] = null;
-                       }
+                       $result = $this->getPropList(
+                               $user,
+                               $attributeManager->getUserEnabledEvents( $user, 
'web' ),
+                               $params['limit'], $params['continue'], 
$params['format']
+                       );
                        $this->getResult()->setIndexedTagName( $result['list'], 
'notification' );
+                       // 'index' is built on top of 'list'
+                       if ( in_array( 'index', $prop ) ) {
+                               $result['index'] = $this->getPropIndex( 
$result['list'] );
+                               $this->getResult()->setIndexedTagName( 
$result['index'], 'id' );
+                       }
                }
 
                if ( in_array( 'count', $prop ) ) {
-                       $rawCount = $notifUser->getNotificationCount();
-                       $result['rawcount'] = $rawCount;
-                       $result['count'] = 
EchoNotificationController::formatNotificationCount( $rawCount );
-               }
-
-               if ( in_array( 'index', $prop ) ) {
-                       $result['index'] = array();
-                       foreach ( array_keys( $result['list'] ) as $key ) {
-                               // Don't include the XML tag name ('_element' 
key)
-                               if ( $key != '_element' ) {
-                                       $result['index'][] = $key;
-                               }
-                       }
-                       $this->getResult()->setIndexedTagName( 
$result['index'], 'id' );
+                       $result += $this->getPropCount( $user );
                }
 
                $this->getResult()->setIndexedTagName( $result, 'notification' 
);
                $this->getResult()->addValue( 'query', $this->getModuleName(), 
$result );
        }
 
+       /**
+       * Internal helper method for getting property 'count' data
+       */
+       protected function getPropCount( User $user ) {
+               $result = array();
+               $notifUser = MWEchoNotifUser::newFromUser( $user );
+               // Always get total count 
+               $rawCount = $notifUser->getNotificationCount();
+               $result['rawcount'] = $rawCount;
+               $result['count'] = 
EchoNotificationController::formatNotificationCount( $rawCount );
+               return $result;
+       }
+
+       /**
+        * Internal helper method for getting property 'list' data
+        */
+       protected function getPropList( User $user, array $eventTypesToLoad, 
$limit, $continue, $format ) {
+               $result = array(
+                       'list' => array(),
+                       'continue' => null
+               );
+
+               // Fetch the result for the event types above
+               $notifMapper = new EchoNotificationMapper( 
MWEchoDbFactory::newFromDefault() );
+               $notifs = $notifMapper->fetchByUser( $user, $limit + 1, 
$continue, $eventTypesToLoad );
+               foreach ( $notifs as $notif ) {
+                       $result['list'][$notif->getEvent()->getID()] = 
EchoDataOutputFormatter::formatOutput( $notif, $format, $user );
+               }
+
+               if ( count( $result['list'] ) > $limit ) {
+                       $lastItem = array_pop( $result['list'] );
+                       $result['continue'] = $lastItem['timestamp']['utcunix'] 
. '|' . $lastItem['id'];
+               }
+
+               return $result;
+       }
+
+       /**
+        * Internal helper method for getting property 'index' data
+        */
+       protected function getPropIndex( $list ) {
+               $result = array();
+               foreach ( array_keys( $list ) as $key ) {
+                       // Don't include the XML tag name ('_element' key)
+                       if ( $key != '_element' ) {
+                               $result[] = $key;
+                       }
+               }
+               return $result;
+       }
+
+       /**
+        * Get a list of notifications based on the passed parameters
+        *
+        * @param $user User the user to get notifications for
+        * @param $format string|bool false to not format any notifications, 
string to a specific output format
+        * @param $limit int The maximum number of notifications to return
+        * @param $continue string Used for offset
+        *
+        * @return array
+        */
+       public static function getNotifications( $user, $format = false, $limit 
= 20, $continue = null ) {
+               global $wgEchoBackend;
+
+               $output = array();
+
+               // TODO: Make 'web' based on a new API param?
+               $res = $wgEchoBackend->loadNotifications( $user, $limit, 
$continue, 'web' );
+
+               foreach ( $res as $row ) {
+                       $event = EchoEvent::newFromRow( $row );
+                       if ( $row->notification_bundle_base && 
$row->notification_bundle_display_hash ) {
+                               $event->setBundleHash( 
$row->notification_bundle_display_hash );
+                       }
+
+                       $timestampMw = self::getUserLocalTime( $user, 
$row->notification_timestamp );
+
+                       // Start creating date section header
+                       $now = wfTimestamp();
+                       $dateFormat = substr( $timestampMw, 0, 8 );
+                       if ( substr( self::getUserLocalTime( $user, $now ), 0, 
8 ) === $dateFormat ) {
+                               // 'Today'
+                               $date = wfMessage( 'echo-date-today' 
)->escaped();
+                       } elseif ( substr( self::getUserLocalTime( $user, $now 
- 86400 ), 0, 8 ) === $dateFormat ) {
+                               // 'Yesterday'
+                               $date = wfMessage( 'echo-date-yesterday' 
)->escaped();
+                       } else {
+                               // 'May 10' or '10 May' (depending on user's 
date format preference)
+                               $lang = 
RequestContext::getMain()->getLanguage();
+                               $dateFormat = $lang->getDateFormatString( 
'pretty', $user->getDatePreference() ?: 'default' );
+                               $date = $lang->sprintfDate( $dateFormat, 
$timestampMw );
+                       }
+                       // End creating date section header
+
+                       $thisEvent = array(
+                               'id' => $event->getId(),
+                               'type' => $event->getType(),
+                               'category' => $event->getCategory(),
+                               'timestamp' => array(
+                                       // UTC timestamp in UNIX format used 
for loading more notification
+                                       'utcunix' => wfTimestamp( TS_UNIX, 
$row->notification_timestamp ),
+                                       'unix' => self::getUserLocalTime( 
$user, $row->notification_timestamp, TS_UNIX ),
+                                       'mw' => $timestampMw,
+                                       'date' => $date
+                               ),
+                       );
+
+                       if ( $event->getVariant() ) {
+                               $thisEvent['variant'] = $event->getVariant();
+                       }
+
+                       if ( $event->getTitle() ) {
+                               $thisEvent['title'] = array(
+                                       'full' => 
$event->getTitle()->getPrefixedText(),
+                                       'namespace' => 
$event->getTitle()->getNSText(),
+                                       'namespace-key' => 
$event->getTitle()->getNamespace(),
+                                       'text' => $event->getTitle()->getText(),
+                               );
+                       }
+
+                       if ( $event->getAgent() ) {
+                               if ( $event->userCan( Revision::DELETED_USER, 
$user ) ) {
+                                       $thisEvent['agent'] = array(
+                                               'id' => 
$event->getAgent()->getId(),
+                                               'name' => 
$event->getAgent()->getName(),
+                                       );
+                               } else {
+                                       $thisEvent['agent'] = array( 
'userhidden' => '' );
+                               }
+                       }
+
+                       if ( $row->notification_read_timestamp ) {
+                               $thisEvent['read'] = 
$row->notification_read_timestamp;
+                       }
+
+                       if ( $format ) {
+                               $thisEvent['*'] = 
EchoNotificationController::formatNotification(
+                                       $event, $user, $format );
+                       }
+
+                       $output[$event->getID()] = $thisEvent;
+               }
+
+               return $output;
+       }
+
+       /**
+        * Internal helper function for converting UTC timezone to a user's 
timezone
+        *
+        * @param $user User
+        * @param $ts string
+        * @param $format int output format
+        *
+        * @return string
+        */
+       private static function getUserLocalTime( $user, $ts, $format = TS_MW ) 
{
+               $timestamp = new MWTimestamp( $ts );
+               $timestamp->offsetForUser( $user );
+               return $timestamp->getTimestamp( $format );
+       }
+
        public function getAllowedParams() {
                return array(
                        'prop' => array(
diff --git a/controller/NotificationController.php 
b/controller/NotificationController.php
index 638cb75..c391659 100644
--- a/controller/NotificationController.php
+++ b/controller/NotificationController.php
@@ -8,108 +8,6 @@
        static protected $userWhitelist;
 
        /**
-        * Get the enabled events for a user, which excludes user-dismissed 
events
-        * from the general enabled events
-        * @param $user User
-        * @param $outputFormat string
-        * @return array
-        */
-       public static function getUserEnabledEvents( $user, $outputFormat ) {
-               global $wgEchoNotifications;
-               $eventTypesToLoad = $wgEchoNotifications;
-               foreach ( $eventTypesToLoad as $eventType => $eventData ) {
-                       $category = self::getNotificationCategory( $eventType );
-                       // Make sure the user is eligible to recieve this type 
of notification
-                       if ( !self::getCategoryEligibility( $user, $category ) 
) {
-                               unset( $eventTypesToLoad[$eventType] );
-                       }
-                       if ( !$user->getOption( 'echo-subscriptions-' . 
$outputFormat . '-' . $category ) ) {
-                               unset( $eventTypesToLoad[$eventType] );
-                       }
-               }
-               return array_keys( $eventTypesToLoad );
-       }
-
-       /**
-        * See if a user is eligible to recieve a certain type of notification
-        * (based on user groups, not user preferences)
-        *
-        * @param $user User object
-        * @param $notificationType string A notification type defined in 
$wgEchoNotifications
-        * @return boolean
-        */
-       public static function getNotificationEligibility( $user, 
$notificationType ) {
-               $category = self::getNotificationCategory( $notificationType );
-               return self::getCategoryEligibility( $user, $category );
-       }
-
-       /**
-        * See if a user is eligible to recieve a certain type of notification
-        * (based on user groups, not user preferences)
-        *
-        * @param $user User object
-        * @param $category string A notification category defined in 
$wgEchoNotificationCategories
-        * @return boolean
-        */
-       public static function getCategoryEligibility( $user, $category ) {
-               global $wgEchoNotificationCategories;
-               $usersGroups = $user->getGroups();
-               if ( isset( 
$wgEchoNotificationCategories[$category]['usergroups'] ) ) {
-                       $allowedGroups = 
$wgEchoNotificationCategories[$category]['usergroups'];
-                       if ( !array_intersect( $usersGroups, $allowedGroups ) ) 
{
-                               return false;
-                       }
-               }
-               return true;
-       }
-
-       /**
-        * Get the priority for a specific notification type
-        *
-        * @param $notificationType string A notification type defined in 
$wgEchoNotifications
-        * @return integer From 1 to 10 (10 is default)
-        */
-       public static function getNotificationPriority( $notificationType ) {
-               $category = self::getNotificationCategory( $notificationType );
-               return self::getCategoryPriority( $category );
-       }
-
-       /**
-        * Get the priority for a notification category
-        *
-        * @param $category string A notification category defined in 
$wgEchoNotificationCategories
-        * @return integer From 1 to 10 (10 is default)
-        */
-       public static function getCategoryPriority( $category ) {
-               global $wgEchoNotificationCategories;
-               if ( isset( 
$wgEchoNotificationCategories[$category]['priority'] ) ) {
-                       $priority = 
$wgEchoNotificationCategories[$category]['priority'];
-                       if ( $priority >= 1 && $priority <= 10 ) {
-                               return $priority;
-                       }
-               }
-               return 10;
-       }
-
-       /**
-        * Get the notification category for a notification type
-        *
-        * @param $notificationType string A notification type defined in 
$wgEchoNotifications
-        * @return String The name of the notification category or 'other' if no
-        *     category is explicitly assigned.
-        */
-       public static function getNotificationCategory( $notificationType ) {
-               global $wgEchoNotifications, $wgEchoNotificationCategories;
-               if ( isset( $wgEchoNotifications[$notificationType]['category'] 
) ) {
-                       $category = 
$wgEchoNotifications[$notificationType]['category'];
-                       if ( isset( $wgEchoNotificationCategories[$category] ) 
) {
-                               return $category;
-                       }
-               }
-               return 'other';
-       }
-
-       /**
         * Format the notification count with Language::formatNum().  In 
addition, for large count,
         * return abbreviated version, e.g. 99+
         * @param $count int
diff --git a/includes/AttributeManager.php b/includes/AttributeManager.php
new file mode 100644
index 0000000..c7b85fe
--- /dev/null
+++ b/includes/AttributeManager.php
@@ -0,0 +1,153 @@
+<?php
+
+/**
+ * An object that manages attributes of echo notifications: category, 
elegibility,
+ * group, section etc.
+ */
+class EchoAttributeManager {
+
+       /**
+        * @var array
+        */
+       protected $notifications;
+
+       /**
+        * @var array
+        */
+       protected $categories;
+
+       /**
+        * An array of EchoAttributeManager instance created from global 
variables
+        * @param EchoAttributeManager[]
+        */
+       protected static $globalVarInstance = array();
+
+       /**
+        * @param array notification attributes
+        * @param array notification categories
+        */
+       public function __construct( array $notifications, array $categories ) {
+               // Extensions can define their own notifications and categories
+               $this->notifications = $notifications;
+               $this->categories = $categories;
+       }
+
+       /**
+        * Create an instance from global variables
+        * @return EchoAttributeManager
+        */
+       public static function newFromGlobalVars() {
+               global $wgEchoNotifications, $wgEchoNotificationCategories;
+
+               // Unit test may alter the global data for test purpose
+               if ( defined( 'MW_PHPUNIT_TEST' ) && MW_PHPUNIT_TEST ) {
+                       return new self( $wgEchoNotifications, 
$wgEchoNotificationCategories );
+               }
+               // A job queue job may run against different wikis, the 
singleton
+               // instance should be a per wiki singleton
+               $wikiId = wfWikiId();
+               if ( !isset( self::$globalVarInstance[$wikiId] ) ) {
+                       self::$globalVarInstance[$wikiId] = new self(
+                               $wgEchoNotifications,
+                               $wgEchoNotificationCategories
+                       );
+               }
+               return self::$globalVarInstance[$wikiId];
+       }
+
+       /**
+        * Get the enabled events for a user, which excludes user-dismissed 
events
+        * from the general enabled events
+        * @param User
+        * @param string web/email
+        * @return string[]
+        */
+       public function getUserEnabledEvents( User $user, $outputFormat ) {
+               $eventTypesToLoad = $this->notifications;
+               foreach ( $eventTypesToLoad as $eventType => $eventData ) {
+                       $category = $this->getNotificationCategory( $eventType 
);
+                       // Make sure the user is eligible to recieve this type 
of notification
+                       if ( !$this->getCategoryEligibility( $user, $category ) 
) {
+                               unset( $eventTypesToLoad[$eventType] );
+                       }
+                       if ( !$user->getOption( 'echo-subscriptions-' . 
$outputFormat . '-' . $category ) ) {
+                               unset( $eventTypesToLoad[$eventType] );
+                       }
+               }
+               $eventTypes = array_keys( $eventTypesToLoad );
+
+               return $eventTypes;
+       }
+
+       /**
+        * Get alert notification event.  Notifications without a section 
attributes
+        * default to section alert
+        * @return array
+        */
+       public function getEvents() {
+               return array_keys( $this->notifications );
+       }
+
+       /**
+        * See if a user is eligible to recieve a certain type of notification
+        * (based on user groups, not user preferences)
+        *
+        * @param User
+        * @param string A notification category defined in 
$wgEchoNotificationCategories
+        * @return boolean
+        */
+       public function getCategoryEligibility( $user, $category ) {
+               $usersGroups = $user->getGroups();
+               if ( isset( $this->categories[$category]['usergroups'] ) ) {
+                       $allowedGroups = 
$this->categories[$category]['usergroups'];
+                       if ( !array_intersect( $usersGroups, $allowedGroups ) ) 
{
+                               return false;
+                       }
+               }
+               return true;
+       }
+
+       /**
+        * Get the priority for a specific notification type
+        *
+        * @param string A notification type defined in $wgEchoNotifications
+        * @return integer From 1 to 10 (10 is default)
+        */
+       public function getNotificationPriority( $notificationType ) {
+               $category = $this->getNotificationCategory( $notificationType );
+               return $this->getCategoryPriority( $category );
+       }
+
+       /**
+        * Get the priority for a notification category
+        *
+        * @param string A notification category defined in 
$wgEchoNotificationCategories
+        * @return integer From 1 to 10 (10 is default)
+        */
+       public function getCategoryPriority( $category ) {
+               if ( isset( $this->categories[$category]['priority'] ) ) {
+                       $priority = $this->categories[$category]['priority'];
+                       if ( $priority >= 1 && $priority <= 10 ) {
+                               return $priority;
+                       }
+               }
+               return 10;
+       }
+
+       /**
+        * Get the notification category for a notification type
+        *
+        * @param string A notification type defined in $wgEchoNotifications
+        * @return string The name of the notification category or 'other' if no
+        *     category is explicitly assigned.
+        */
+       public function getNotificationCategory( $notificationType ) {
+               if ( isset( $this->notifications[$notificationType]['category'] 
) ) {
+                       $category = 
$this->notifications[$notificationType]['category'];
+                       if ( isset( $this->categories[$category] ) ) {
+                               return $category;
+                       }
+               }
+               return 'other';
+       }
+}
diff --git a/includes/NotifUser.php b/includes/NotifUser.php
index 670ed85..4194a14 100644
--- a/includes/NotifUser.php
+++ b/includes/NotifUser.php
@@ -51,7 +51,7 @@
                        new EchoUserNotificationGateway( $user, 
MWEchoDbFactory::newFromDefault() )
                );
        }
-
+       
        /**
         * Clear talk page notification when users visit their talk pages.  This
         * only resets if the notification count is less than max notification
@@ -122,6 +122,7 @@
         *
         * @param $cached bool Set to false to bypass the cache.
         * @param $dbSource int use master or slave database to pull count
+        * @param $section string
         * @return integer: Number of unread notifications.
         */
        public function getNotificationCount( $cached = true, $dbSource = 
DB_SLAVE ) {
@@ -131,13 +132,19 @@
                        return 0;
                }
 
-               $memcKey = wfMemcKey( 'echo-notification-count', 
$this->mUser->getId(), $wgEchoConfig['version'] );
+               $memcKey = wfMemcKey(
+                       'echo-notification-count',
+                       $this->mUser->getId(),
+                       $wgEchoConfig['version']
+               );
 
                if ( $cached && $this->cache->get( $memcKey ) !== false ) {
                        return (int)$this->cache->get( $memcKey );
                }
 
-               $count = $this->userNotifGateway->getNotificationCount( 
$dbSource );
+               $attributeManager = EchoAttributeManager::newFromGlobalVars();
+               $eventTypesToLoad = $attributeManager->getUserEnabledEvents( 
$this->mUser, 'web' );
+               $count = $this->userNotifGateway->getNotificationCount( 
$dbSource, $eventTypesToLoad );
 
                $this->cache->set( $memcKey, $count, 86400 );
 
diff --git a/includes/gateway/UserNotificationGateway.php 
b/includes/gateway/UserNotificationGateway.php
index b138a6c..1a1ac17 100644
--- a/includes/gateway/UserNotificationGateway.php
+++ b/includes/gateway/UserNotificationGateway.php
@@ -74,16 +74,16 @@
        }
 
        /**
-        * @param $dbSource string use master or slave storage to pull count
+        * Get notification count for the types specified
+        * @param int use master or slave storage to pull count
+        * @param array event types to retrieve
         * @return int
         */
-       public function getNotificationCount( $dbSource ) {
+       public function getNotificationCount( $dbSource, array 
$eventTypesToLoad = array() ) {
                // double check
                if ( !in_array( $dbSource, array( DB_SLAVE, DB_MASTER ) ) ) {
                        $dbSource = DB_SLAVE;
                }
-
-               $eventTypesToLoad = 
EchoNotificationController::getUserEnabledEvents( $this->user, 'web' );
 
                if ( !$eventTypesToLoad ) {
                        return 0;
diff --git a/includes/mapper/NotificationMapper.php 
b/includes/mapper/NotificationMapper.php
index fd1756d..cdca78d 100644
--- a/includes/mapper/NotificationMapper.php
+++ b/includes/mapper/NotificationMapper.php
@@ -91,17 +91,23 @@
         * @param User the user to get notifications for
         * @param int The maximum number of notifications to return
         * @param string Used for offset
-        * @param string Notification distribution type ( web, email, etc.)
+        * @param array Event types to load
         * @return EchoNotification[]
         */
-       public function fetchByUser( User $user, $limit, $continue, 
$distributionType = 'web' ) {
+       public function fetchByUser( User $user, $limit, $continue, array 
$eventTypesToLoad = array() ) {
                $dbr = $this->dbFactory->getEchoDb( DB_SLAVE );
 
-               $eventTypesToLoad = 
EchoNotificationController::getUserEnabledEvents( $user, $distributionType );
                if ( !$eventTypesToLoad ) {
                        return array();
                }
 
+               // There is a problem with querying by event type, if a user 
has only one or none
+               // flow notification and huge amount other notications, the 
lookup of only flow
+               // notification will result in a slow query.  Luckily users 
won't have that many
+               // notifications.  We should have some cron job to remove old 
notifications so
+               // the notification volume is in a reasonable amount for such 
case.  The other option
+               // is to denormalize notification table with event_type and 
lookup index.
+               //
                // Look for notifications with base = 1
                $conds = array(
                        'notification_user' => $user->getID(),
diff --git a/model/Event.php b/model/Event.php
index b66343f..48e7b62 100644
--- a/model/Event.php
+++ b/model/Event.php
@@ -421,7 +421,8 @@
         * @return string
         */
        public function getCategory() {
-               return EchoNotificationController::getNotificationCategory( 
$this->type );
+               $attributeManager = EchoAttributeManager::newFromGlobalVars();
+               return $attributeManager->getNotificationCategory( $this->type 
);
        }
 
        public function setType( $type ) {
diff --git a/special/SpecialNotifications.php b/special/SpecialNotifications.php
index 4ca78bd..1f7f69f 100644
--- a/special/SpecialNotifications.php
+++ b/special/SpecialNotifications.php
@@ -20,13 +20,14 @@
 
                $user = $this->getUser();
                if ( $user->isAnon() ) {
-                       $notificationsPageName = 
$this->getPageTitle()->getPrefixedDBkey();
-                       $returnTo = array( 'returnto' => $notificationsPageName 
);
-                       $signupTitle = SpecialPage::getTitleFor( 'UserLogin', 
'signup' );
-                       $signupURL = $signupTitle->getFullURL( $returnTo );
-                       $loginTitle = SpecialPage::getTitleFor( 'UserLogin' );
-                       $loginURL = $loginTitle->getFullURL( $returnTo );
-                       $anonMsgHtml = $this->msg( 'echo-anon', $signupURL, 
$loginURL )->parse();
+                       // return to this title upon login
+                       $returnTo = array( 'returnto' => 
$this->getPageTitle()->getPrefixedDBkey() );
+                       // the html message for anon users
+                       $anonMsgHtml = $this->msg(
+                               'echo-anon',
+                               SpecialPage::getTitleFor( 'UserLogin', 'signup' 
)->getFullURL( $returnTo ),
+                               SpecialPage::getTitleFor( 'UserLogin' 
)->getFullURL( $returnTo )
+                       )->parse();
                        $out->addHTML( Html::rawElement( 'span', array( 'class' 
=> 'plainlinks' ), $anonMsgHtml ) );
                        return;
                }
@@ -40,7 +41,14 @@
                // Pull the notifications
                $notif = array();
                $notificationMapper = new EchoNotificationMapper( 
MWEchoDbFactory::newFromDefault() );
-               $notifications = $notificationMapper->fetchByUser( $user, 
self::$displayNum + 1, $continue, 'web' );
+
+               $attributeManager = EchoAttributeManager::newFromGlobalVars();
+               $notifications = $notificationMapper->fetchByUser(
+                       $user,
+                       /* $limit = */self::$displayNum + 1,
+                       $continue,
+                       $attributeManager->getUserEnabledEvents( $user, 'web' )
+               );
                foreach ( $notifications as $notification ) {
                        $notif[] = EchoDataOutputFormatter::formatOutput( 
$notification, 'html', $user );
                }
diff --git a/tests/EmailFormatterTest.php b/tests/EmailFormatterTest.php
index ef69e5b..345c8ed 100644
--- a/tests/EmailFormatterTest.php
+++ b/tests/EmailFormatterTest.php
@@ -69,6 +69,7 @@
                $methods = get_class_methods( 'EchoEvent' );
                $methods = array_diff( $methods, array( 'userCan', 
'getLinkMessage', 'getLinkDestination' ) );
 
+               $attribManager = EchoAttributeManager::newFromGlobalVars();
                $event = $this->getMockBuilder( 'EchoEvent' )
                        ->disableOriginalConstructor()
                        ->setMethods( $methods )
@@ -78,7 +79,7 @@
                        ->will( $this->returnValue( $type ) );
                $event->expects( $this->any() )
                        ->method( 'getCategory' )
-                       ->will( $this->returnValue( 
EchoNotificationController::getNotificationCategory( $type ) ) );
+                       ->will( $this->returnValue( 
$attribManager->getNotificationCategory( $type ) ) );
                return $event;
        }
 
diff --git a/tests/includes/AttributeManagerTest.php 
b/tests/includes/AttributeManagerTest.php
new file mode 100644
index 0000000..dc0157d
--- /dev/null
+++ b/tests/includes/AttributeManagerTest.php
@@ -0,0 +1,175 @@
+<?php
+
+class EchoAttributeManagerTest extends MediaWikiTestCase {
+
+       public function testNewFromGlobalVars() {
+               $this->assertInstanceOf( 'EchoAttributeManager', 
EchoAttributeManager::newFromGlobalVars() );
+       }
+
+       public function testGetCategoryEligibility() {
+               $notif = array(
+                       'event_one' => array (
+                               'category' => 'category_one'
+                       ),
+               );
+               $category = array(
+                       'category_one' => array (
+                               'priority' => 10
+                       )
+               );
+               $manager = new EchoAttributeManager( $notif, $category );
+               $this->assertTrue( $manager->getCategoryEligibility( 
$this->mockUser(), 'category_one' ) );
+               $category = array(
+                       'category_one' => array (
+                               'priority' => 10,
+                               'usergroups' => array(
+                                       'sysop'
+                               )
+                       )
+               );
+               $manager = new EchoAttributeManager( $notif, $category );
+               $this->assertFalse( $manager->getCategoryEligibility( 
$this->mockUser(), 'category_one' ) );
+       }
+
+       public function testGetNotificationCategory() {
+               $notif = array(
+                       'event_one' => array (
+                               'category' => 'category_one'
+                       ),
+               );
+               $category = array(
+                       'category_one' => array (
+                               'priority' => 10
+                       )
+               );
+               $manager = new EchoAttributeManager( $notif, $category );
+               $this->assertEquals( $manager->getNotificationCategory( 
'event_one' ), 'category_one' );
+
+               $manager = new EchoAttributeManager( $notif, array() );
+               $this->assertEquals( $manager->getNotificationCategory( 
'event_one' ), 'other' );
+
+               $notif = array(
+                       'event_one' => array (
+                               'category' => 'category_two'
+                       ),
+               );
+               $category = array(
+                       'category_one' => array (
+                               'priority' => 10
+                       )
+               );
+               $manager = new EchoAttributeManager( $notif, $category );
+               $this->assertEquals( $manager->getNotificationCategory( 
'event_one' ), 'other' );
+       }
+
+       public function testGetCategoryPriority() {
+               $notif = array(
+                       'event_one' => array (
+                               'category' => 'category_two'
+                       ),
+               );
+               $category = array(
+                       'category_one' => array (
+                               'priority' => 6
+                       ),
+                       'category_two' => array (
+                               'priority' => 100
+                       ),
+                       'category_three' => array (
+                               'priority' => -10
+                       ),
+                       'category_four' => array ()
+               );
+               $manager = new EchoAttributeManager( $notif, $category );
+               $this->assertEquals( 6, $manager->getCategoryPriority( 
'category_one' ) );
+               $this->assertEquals( 10, $manager->getCategoryPriority( 
'category_two' ) );
+               $this->assertEquals( 10, $manager->getCategoryPriority( 
'category_three' ) );
+               $this->assertEquals( 10, $manager->getCategoryPriority( 
'category_four' ) );
+       }
+
+       public function testGetNotificationPriority() {
+               $notif = array(
+                       'event_one' => array (
+                               'category' => 'category_one'
+                       ),
+                       'event_two' => array (
+                               'category' => 'category_two'
+                       ),
+                       'event_three' => array (
+                               'category' => 'category_three'
+                       ),
+                       'event_four' => array (
+                               'category' => 'category_four'
+                       )
+               );
+               $category = array(
+                       'category_one' => array (
+                               'priority' => 6
+                       ),
+                       'category_two' => array (
+                               'priority' => 100
+                       ),
+                       'category_three' => array (
+                               'priority' => -10
+                       ),
+                       'category_four' => array ()
+               );
+               $manager = new EchoAttributeManager( $notif, $category );
+               $this->assertEquals( 6, $manager->getNotificationPriority( 
'event_one' ) );
+               $this->assertEquals( 10, $manager->getNotificationPriority( 
'event_two' ) );
+               $this->assertEquals( 10, $manager->getNotificationPriority( 
'event_three' ) );
+               $this->assertEquals( 10, $manager->getNotificationPriority( 
'event_four' ) );
+       }
+
+       public function testGetUserEnabledEvents() {
+               $notif = array(
+                       'event_one' => array (
+                               'category' => 'category_one'
+                       ),
+                       'event_two' => array (
+                               'category' => 'category_two'
+                       ),
+                       'event_three' => array (
+                               'category' => 'category_three'
+                       ),
+               );
+               $category = array(
+                       'category_one' => array (
+                               'priority' => 10,
+                               'usergroups' => array(
+                                       'sysop'
+                               )
+                       ),
+                       'category_two' => array (
+                               'priority' => 10,
+                               'usergroups' => array(
+                                       'echo_group'
+                               )
+                       ),
+                       'category_three' => array (
+                               'priority' => 10,
+                       ),
+               );
+               $manager = new EchoAttributeManager( $notif, $category );
+               $this->assertEquals( $manager->getUserEnabledEvents( 
$this->mockUser(), 'web' ), array( 'event_two', 'event_three' ) );
+       }
+
+       /**
+        * Mock object of User
+        */
+       protected function mockUser() {
+               $user = $this->getMockBuilder( 'User' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $user->expects( $this->any() )
+                       ->method( 'getID' )
+                       ->will( $this->returnValue( 1 ) );
+               $user->expects( $this->any() )
+                       ->method( 'getOption' )
+                       ->will( $this->returnValue( true ) );
+               $user->expects( $this->any() )
+                       ->method( 'getGroups' )
+                       ->will( $this->returnValue( array( 'echo_group' ) ) );
+               return $user;
+       }
+}
diff --git a/tests/includes/gateway/UserNotificationGatewayTest.php 
b/tests/includes/gateway/UserNotificationGatewayTest.php
index b4f807a..8ae94f6 100644
--- a/tests/includes/gateway/UserNotificationGatewayTest.php
+++ b/tests/includes/gateway/UserNotificationGatewayTest.php
@@ -27,35 +27,21 @@
        }
 
        public function testGetNotificationCount() {
-               global $wgEchoNotificationCategories;
-               $previous = $wgEchoNotificationCategories;
-
-               // Alter the category group so the user is always elegible to
-               // view some notification types.
-               foreach ( $wgEchoNotificationCategories as &$value ) {
-                       $value['usergroups'] = array( 'echo_group' );
-               }
-               unset( $value );
-
-               // successful select
+               // unsuccessful select
                $gateway = new EchoUserNotificationGateway( $this->mockUser(), 
$this->mockMWEchoDbFactory( array( 'select' => false ) ) );
-               $this->assertEquals( 0, $gateway->getNotificationCount( 
DB_SLAVE ) );
+               $this->assertEquals( 0, $gateway->getNotificationCount( 
DB_SLAVE, array( 'event_one' ) ) );
+
+               // successful select of alert
+               $gateway = new EchoUserNotificationGateway( $this->mockUser(), 
$this->mockMWEchoDbFactory( array( 'select' => array( 1, 2 ) ) ) );
+               $this->assertEquals( 2, $gateway->getNotificationCount( 
DB_SLAVE, array( 'event_one', 'event_two' ) ) );
+
+               // there is event, should return 0
+               $gateway = new EchoUserNotificationGateway( $this->mockUser(), 
$this->mockMWEchoDbFactory( array( 'select' => array( 1, 2 ) ) ) );
+               $this->assertEquals( 0, $gateway->getNotificationCount( 
DB_SLAVE, array() ) );
 
                // successful select
                $gateway = new EchoUserNotificationGateway( $this->mockUser(), 
$this->mockMWEchoDbFactory( array( 'select' => array( 1, 2, 3 ) ) ) );
-               $this->assertEquals( 3, $gateway->getNotificationCount( 
DB_SLAVE ) );
-
-               // Alter the category group so the user is not elegible to
-               // view any notification types.
-               foreach ( $wgEchoNotificationCategories as &$value ) {
-                       $value['usergroups'] = array( 'sysop' );
-               }
-               unset( $value );
-
-               $gateway = new EchoUserNotificationGateway( $this->mockUser(), 
$this->mockMWEchoDbFactory( array( 'select' => array( 1, 2, 3 ) ) ) );
-               $this->assertEquals( 0, $gateway->getNotificationCount( 
DB_SLAVE ) );
-
-               $wgEchoNotificationCategories = $previous;
+               $this->assertEquals( 3, $gateway->getNotificationCount( 
DB_SLAVE, array( 'event_one' ) ) );
        }
 
        public function testGetUnreadNotifications() {
@@ -75,7 +61,7 @@
        /**
         * Mock object of User
         */
-       protected function mockUser() {
+       protected function mockUser( $group = 'echo_group' ) {
                $user = $this->getMockBuilder( 'User' )
                        ->disableOriginalConstructor()
                        ->getMock();
@@ -87,7 +73,7 @@
                        ->will( $this->returnValue( true ) );
                $user->expects( $this->any() )
                        ->method( 'getGroups' )
-                       ->will( $this->returnValue( array( 'echo_group' ) ) );
+                       ->will( $this->returnValue( array( $group ) ) );
                return $user;
        }
 
diff --git a/tests/includes/mapper/NotificationMapperTest.php 
b/tests/includes/mapper/NotificationMapperTest.php
index eac422c..224f9df 100644
--- a/tests/includes/mapper/NotificationMapperTest.php
+++ b/tests/includes/mapper/NotificationMapperTest.php
@@ -44,8 +44,13 @@
                        )
                );
                $notifMapper = new EchoNotificationMapper( 
$this->mockMWEchoDbFactory( array ( 'select' => $dbResult ) ) );
-               $res = $notifMapper->fetchByUser( $this->mockUser(), 10, '' );
+               $res = $notifMapper->fetchByUser( $this->mockUser(), 10, '', 
array() );
+               $this->assertEmpty( $res  );
+
+               $notifMapper = new EchoNotificationMapper( 
$this->mockMWEchoDbFactory( array ( 'select' => $dbResult ) ) );
+               $res = $notifMapper->fetchByUser( $this->mockUser(), 10, '', 
array( 'test_event' ) );
                $this->assertTrue( is_array( $res ) );
+               $this->assertGreaterThan( 0, count( $res ) );
                foreach ( $res as $row ) {
                        $this->assertInstanceOf( 'EchoNotification', $row  );
                }

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

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

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

Reply via email to