diff --git a/Echo.php b/Echo.php index 32c4c015a..9b30c9718 100644 --- a/Echo.php +++ b/Echo.php @@ -89,6 +89,7 @@ $wgAutoloadClasses['EchoHTMLEmailDecorator'] = $dir . 'includes/EmailFormatter.p // Internal stuff $wgAutoloadClasses['EchoNotifier'] = $dir . 'Notifier.php'; +$wgAutoloadClasses['EchoUserLocator'] = $dir . 'includes/UserLocator.php'; $wgAutoloadClasses['EchoNotificationController'] = $dir . 'controller/NotificationController.php'; $wgAutoloadClasses['EchoDiscussionParser'] = $dir . 'includes/DiscussionParser.php'; $wgAutoloadClasses['EchoDiffParser'] = $dir . 'includes/DiffParser.php'; @@ -442,6 +443,9 @@ $wgEchoNotifications = array( 'icon' => 'site', ), 'edit-user-talk' => array( + 'user-locators' => array( + 'EchoUserLocator::locateTalkPageOwner', + ), 'primary-link' => array( 'message' => 'notification-link-text-view-message', 'destination' => 'section' ), 'secondary-link' => array( 'message' => 'notification-link-text-view-changes', 'destination' => 'diff' ), 'category' => 'edit-user-talk', diff --git a/Hooks.php b/Hooks.php index ae38e5748..e1257a25c 100644 --- a/Hooks.php +++ b/Hooks.php @@ -134,19 +134,6 @@ class EchoHooks { */ public static function getDefaultNotifiedUsers( $event, &$users ) { switch ( $event->getType() ) { - // Everyone deserves to know when something happens - // on their user talk page - case 'edit-user-talk': - if ( !$event->getTitle() || !$event->getTitle()->getNamespace() == NS_USER_TALK ) { - break; - } - - $username = $event->getTitle()->getText(); - $user = User::newFromName( $username ); - if ( $user && $user->getId() ) { - $users[$user->getId()] = $user; - } - break; case 'add-comment': case 'add-talkpage-topic': // Handled by EchoDiscussionParser diff --git a/controller/NotificationController.php b/controller/NotificationController.php index c391659bc..49abd9462 100644 --- a/controller/NotificationController.php +++ b/controller/NotificationController.php @@ -87,7 +87,7 @@ class EchoNotificationController { continue; } if ( $blacklisted && !self::isWhitelistedByUser( $event, $user ) ) { - continue; + continue; } wfRunHooks( 'EchoGetNotificationTypes', array( $user, $event, &$notifyTypes ) ); @@ -190,13 +190,27 @@ class EchoNotificationController { /** * Retrieves an array of User objects to be notified for an EchoEvent. * - * @param $event EchoEvent to retrieve users to be notified for. - * @return Array of User objects + * @param EchoEvent $event + * @return array keys are user ids, values are User objects */ - protected static function getUsersToNotifyForEvent( $event ) { - $users = $notifyList = array(); + public static function getUsersToNotifyForEvent( EchoEvent $event ) { + $type = $event->getType(); + // Key notifyList by user id to ensure there are no duplicated users + $notifyList = array(); + + $attributeManager = EchoAttributeManager::newFromGlobalVars(); + foreach ( $attributeManager->getUserLocators( $type ) as $callable ) { + if ( is_callable( $callable ) ) { + $notifyList += call_user_func( $callable, $event ); + } else { + wfDebugLog( __CLASS__, __FUNCTION__ . ": Invalid user-locator returned for $type" ); + } + } + + // hook for injecting more users. + // @deprecated + $users = array(); wfRunHooks( 'EchoGetDefaultNotifiedUsers', array( $event, &$users ) ); - // Make sure there is no duplicated users foreach ( $users as $user ) { $notifyList[$user->getId()] = $user; } @@ -259,7 +273,7 @@ class EchoNotificationController { /** * INTERNAL. Must be public to be callable by the php error handling methods. * - * Converts E_RECOVERABLE_ERROR, such as passing null to a method expecting + * Converts E_RECOVERABLE_ERROR, such as passing null to a method expecting * a non-null object, into exceptions. */ public static function formatterErrorHandler( $errno, $errstr, $errfile, $errline ) { diff --git a/includes/AttributeManager.php b/includes/AttributeManager.php index c7b85fe48..4aac0a390 100644 --- a/includes/AttributeManager.php +++ b/includes/AttributeManager.php @@ -55,6 +55,21 @@ class EchoAttributeManager { return self::$globalVarInstance[$wikiId]; } + /** + * Get the user-locators related to the provided event type + * + * @param string $type + * @return array + */ + public function getUserLocators( $type ) { + if ( isset( $this->notifications[$type]['user-locators'] ) ) { + return (array)$this->notifications[$type]['user-locators']; + } else { + wfDebugLog( 'Echo', __METHOD__ . ": No user-locators configured for $type" ); + return array(); + } + } + /** * Get the enabled events for a user, which excludes user-dismissed events * from the general enabled events diff --git a/includes/UserLocator.php b/includes/UserLocator.php new file mode 100644 index 000000000..9a7294cbb --- /dev/null +++ b/includes/UserLocator.php @@ -0,0 +1,49 @@ +getTitle(); + if ( !$title ) { + return array(); + } + + $dbr = wfGetDB( DB_SLAVE, 'watchlist' ); + $res = $dbr->select( + array( 'watchlist' ), + array( 'wl_user' ), + array( + 'wl_namespace' => $title->getNamespace(), + 'wl_title' => $title->getDBkey(), + ), + __METHOD__ + ); + + $users = array(); + if ( $res ) { + foreach ( $res as $row ) { + $users[$row->wl_user] = User::newFromId( $row->wl_user ); + } + } + + return $users; + } + + public static function locateTalkPageOwner( EchoEvent $event ) { + $title = $event->getTitle(); + if ( !$title || $title->getNamespace() !== NS_USER_TALK ) { + return array(); + } + + $user = User::newFromName( $title->getDBkey() ); + if ( $user && !$user->isAnon() ) { + return array( $user->getId() => $user ); + } else { + return array(); + } + } +} diff --git a/tests/NotificationControllerTest.php b/tests/NotificationControllerTest.php new file mode 100644 index 000000000..30db70ab4 --- /dev/null +++ b/tests/NotificationControllerTest.php @@ -0,0 +1,67 @@ + 123 ); } + ), + + array( + 'merges results of multiple locators', + // expected result + array( 123, 456 ), + // event user locator config + array( + function() { return array( 123 => 123 ); }, + function() { return array( 456 => 456 ); }, + ), + ), + + ); + } + + /** + * @dataProvider getUsersToNotifyForEventProvider + */ + public function testGetUsersToNotifyForEvent( $message, $expect, $locatorConfigForEventType ) { + $this->setMwGlobals( array( + 'wgEchoNotifications' => array( + 'unit-test' => array( + 'user-locators' => $locatorConfigForEventType + ), + ), + ) ); + + $event = $this->getMockBuilder( 'EchoEvent' ) + ->disableOriginalConstructor() + ->getMock(); + $event->expects( $this->any() ) + ->method( 'getType' ) + ->will( $this->returnValue( 'unit-test' ) ); + + $users = EchoNotificationController::getUsersToNotifyForEvent( $event ); + $this->assertEquals( $expect, array_keys( $users ), $message ); + } +} diff --git a/tests/includes/AttributeManagerTest.php b/tests/includes/AttributeManagerTest.php index dc0157d14..b180afdf0 100644 --- a/tests/includes/AttributeManagerTest.php +++ b/tests/includes/AttributeManagerTest.php @@ -6,6 +6,61 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { $this->assertInstanceOf( 'EchoAttributeManager', EchoAttributeManager::newFromGlobalVars() ); } + public static function getUserLocatorsProvider() { + return array( + array( + 'No errors when requesting unknown type', + // expected result + array(), + // event type + 'foo', + // notification configuration + array(), + ), + + array( + 'Returns selected notification configuration', + // expected result + array( 'woot!' ), + // event type + 'magic', + // notification configuration + array( + 'foo' => array( + 'user-locators' => array( 'frown' ), + ), + 'magic' => array( + 'user-locators' => array( 'woot!' ), + ), + ), + ), + + array( + 'Accepts user-locators as string and returns array', + // expected result + array( 'sagen' ), + // event type + 'challah', + // notification configuration + array( + 'challah' => array( + 'user-locators' => 'sagen', + ), + ), + ), + ); + } + + /** + * @dataProvider getUserLocatorsProvider + */ + public function testGetUserLocators( $message, $expect, $type, $notifications) { + $manager = new EchoAttributeManager( $notifications, array() ); + + $result = $manager->getUserLocators( $type ); + $this->assertEquals( $expect, $result, $message ); + } + public function testGetCategoryEligibility() { $notif = array( 'event_one' => array (