diff --git a/Echo.php b/Echo.php index c66cabb07..aa13cf21b 100644 --- a/Echo.php +++ b/Echo.php @@ -83,6 +83,9 @@ $wgHooks['ParserTestTables'][] = 'EchoHooks::onParserTestTables'; $wgHooks['EmailUserComplete'][] = 'EchoHooks::onEmailUserComplete'; $wgHooks['LoginFormValidErrorMessages'][] = 'EchoHooks::onLoginFormValidErrorMessages'; $wgHooks['OutputPageCheckLastModified'][] = 'EchoHooks::onOutputPageCheckLastModified'; +$wgHooks['ArticleDeleteComplete'][] = 'EchoHooks::onArticleDeleteComplete'; +$wgHooks['ArticleUndelete'][] = 'EchoHooks::onArticleUndelete'; + // Extension:UserMerge support $wgHooks['UserMergeAccountFields'][] = 'EchoHooks::onUserMergeAccountFields'; diff --git a/Hooks.php b/Hooks.php index f451c44d4..efd5fe462 100644 --- a/Hooks.php +++ b/Hooks.php @@ -721,12 +721,14 @@ class EchoHooks { continue; } + $linkFromPageId = $linksUpdate->mTitle->getArticleId(); EchoEvent::create( array( 'type' => 'page-linked', 'title' => $title, 'agent' => $user, 'extra' => array( - 'link-from-page-id' => $linksUpdate->mTitle->getArticleId(), + 'target-page' => $linkFromPageId, + 'link-from-page-id' => $linkFromPageId, 'revid' => $revid, ) ) ); @@ -1304,4 +1306,24 @@ class EchoHooks { return true; } + public static function onArticleDeleteComplete( WikiPage &$article, User &$user, $reason, $articleId, Content $content = null, LogEntry $logEntry ) { + \DeferredUpdates::addCallableUpdate( function () use ( $articleId ) { + $eventMapper = new EchoEventMapper(); + $eventIds = $eventMapper->fetchIdsByPage( $articleId ); + EchoModerationController::moderate( $eventIds, true ); + } ); + return true; + } + + public static function onArticleUndelete( Title $title, $create, $comment, $oldPageId ) { + if ( $create ) { + \DeferredUpdates::addCallableUpdate( function () use ( $oldPageId ) { + $eventMapper = new EchoEventMapper(); + $eventIds = $eventMapper->fetchIdsByPage( $oldPageId ); + EchoModerationController::moderate( $eventIds, false ); + } ); + } + return true; + } + } diff --git a/autoload.php b/autoload.php index 65589edd1..a828fc506 100644 --- a/autoload.php +++ b/autoload.php @@ -30,7 +30,7 @@ $wgAutoloadClasses += [ 'EchoContainmentList' => __DIR__ . '/includes/ContainmentSet.php', 'EchoContainmentSet' => __DIR__ . '/includes/ContainmentSet.php', 'EchoDataOutputFormatter' => __DIR__ . '/includes/DataOutputFormatter.php', - 'EchoDeferredMarkAsReadUpdate' => __DIR__ . '/includes/DeferredMarkAsReadUpdate.php', + 'EchoDeferredMarkAsDeletedUpdate' => __DIR__ . '/includes/DeferredMarkAsDeletedUpdate.php', 'EchoDiffGroup' => __DIR__ . '/includes/DiffParser.php', 'EchoDiffParser' => __DIR__ . '/includes/DiffParser.php', 'EchoDiffParserTest' => __DIR__ . '/tests/phpunit/DiffParserTest.php', diff --git a/includes/DataOutputFormatter.php b/includes/DataOutputFormatter.php index 61c7c2feb..d7b866e50 100644 --- a/includes/DataOutputFormatter.php +++ b/includes/DataOutputFormatter.php @@ -136,7 +136,7 @@ class EchoDataOutputFormatter { $formatted = self::formatNotification( $event, $user, $format, $lang ); if ( $formatted === false ) { // Can't display it, so mark it as read - EchoDeferredMarkAsReadUpdate::add( $event, $user ); + EchoDeferredMarkAsDeletedUpdate::add( $event ); return false; } $output['*'] = $formatted; diff --git a/includes/DeferredMarkAsDeletedUpdate.php b/includes/DeferredMarkAsDeletedUpdate.php new file mode 100644 index 000000000..0531e899c --- /dev/null +++ b/includes/DeferredMarkAsDeletedUpdate.php @@ -0,0 +1,72 @@ +addInternal( $event ); + } + + /** + * @param EchoEvent $event + */ + private function addInternal( EchoEvent $event ) { + $this->events[] = $event; + } + + private function filterEventsWithTitleDbLag() { + return array_filter( + $this->events, + function ( EchoEvent $event ) { + if ( !$event->getTitle() && $event->getTitle( true ) ) { + // It is very likely this event was found + // unreaderable because of slave lag. + // Do not moderate it at this time. + LoggerFactory::getInstance( 'Echo' )->debug( + 'EchoDeferredMarkAsDeletedUpdate: Event {eventId} was found unrenderable but its associated title exists on Master. Skipping.', + array( + 'eventId' => $event->getId(), + 'title' => $event->getTitle()->getPrefixedText(), + ) + ); + return false; + } + return true; + } + ); + } + + /** + * Marks all queued notifications as read. + * Satisfies DeferrableUpdate interface + */ + public function doUpdate() { + $events = $this->filterEventsWithTitleDbLag(); + + $eventIds = array_map( + function ( EchoEvent $event ) { + return $event->getId(); + }, + $events + ); + + EchoModerationController::moderate( $eventIds, true ); + $this->events = array(); + } +} diff --git a/includes/DeferredMarkAsReadUpdate.php b/includes/DeferredMarkAsReadUpdate.php deleted file mode 100644 index decde6264..000000000 --- a/includes/DeferredMarkAsReadUpdate.php +++ /dev/null @@ -1,52 +0,0 @@ -addInternal( $event, $user ); - } - - /** - * @param EchoEvent $event - * @param User $user - */ - private function addInternal( EchoEvent $event, User $user ) { - $uid = $user->getId(); - if ( isset( $this->events[$uid] ) ) { - $this->events[$uid]['eventIds'][] = $event->getId(); - } else { - $this->events[$uid] = array( - 'user' => $user, - 'eventIds' => array( $event->getId() ), - ); - } - } - - /** - * Mark's all queue'd notifications as read. - * Satisfies DeferrableUpdate interface - */ - public function doUpdate() { - foreach ( $this->events as $data ) { - MWEchoNotifUser::newFromUser( $data['user'] )->markRead( $data['eventIds'] ); - } - $this->events = array(); - } -} diff --git a/includes/controller/NotificationController.php b/includes/controller/NotificationController.php index 918a9430f..a6fb3ccb9 100644 --- a/includes/controller/NotificationController.php +++ b/includes/controller/NotificationController.php @@ -381,21 +381,6 @@ class EchoNotificationController { return $notify->getIterator(); } - /** - * Event has failed to format for the given user. Mark it as read so - * we do not continue to notify them about this broken event. - * - * @param EchoEvent $event - * @param User $user - */ - protected static function failFormatting( EchoEvent $event, $user ) { - // FIXME: The only issue is that the badge count won't be up to date - // till you refresh the page. Probably we could do this in the browser - // so that if the formatting is empty and the notif is unread, put it - // in the auto-mark-read APIs - EchoDeferredMarkAsReadUpdate::add( $event, $user ); - } - /** * INTERNAL. Must be public to be callable by the php error handling methods. * diff --git a/includes/mapper/EventMapper.php b/includes/mapper/EventMapper.php index 8a27e5fa5..c23179533 100644 --- a/includes/mapper/EventMapper.php +++ b/includes/mapper/EventMapper.php @@ -174,6 +174,52 @@ class EchoEventMapper extends EchoAbstractMapper { return $data; } + /** + * Fetch events associated with a page + * + * @param int $pageId + * @return EchoEvent[] Events + */ + public function fetchByPage( $pageId ) { + $events = array(); + + $dbr = $this->dbFactory->getEchoDb( DB_SLAVE ); + $res = $dbr->select( + array( 'echo_event', 'echo_target_page' ), + array( 'echo_event.*' ), + array( + 'etp_page' => $pageId + ), + __METHOD__, + array( 'GROUP BY' => 'etp_event' ), + array( 'echo_target_page' => array( 'JOIN', 'event_id=etp_event' ) ) + ); + if ( $res ) { + foreach ( $res as $row ) { + $events[] = EchoEvent::newFromRow( $row ); + } + } + + return $events; + } + + /** + * Fetch event IDs associated with a page + * + * @param int $pageId + * @return int[] Event IDs + */ + public function fetchIdsByPage( $pageId ) { + $events = $this->fetchByPage( $pageId ); + $eventIds = array_map( + function ( EchoEvent $event ) { + return $event->getId(); + }, + $events + ); + return $eventIds; + } + /** * Fetch events unread by a user and associated with a page * diff --git a/includes/mapper/NotificationMapper.php b/includes/mapper/NotificationMapper.php index e1284565f..6cc306482 100644 --- a/includes/mapper/NotificationMapper.php +++ b/includes/mapper/NotificationMapper.php @@ -392,7 +392,7 @@ class EchoNotificationMapper extends EchoAbstractMapper { $res = $dbr->select( array( 'echo_notification' ), - array( 'userId' => 'notification_user' ), + array( 'userId' => 'DISTINCT notification_user' ), array( 'notification_event' => $eventIds ), diff --git a/includes/model/Event.php b/includes/model/Event.php index 325d8ad50..8641d7001 100644 --- a/includes/model/Event.php +++ b/includes/model/Event.php @@ -465,9 +465,10 @@ class EchoEvent extends EchoAbstractEntity implements Bundleable { } /** - * @return Title|null + * @param bool $fromMaster + * @return null|Title */ - public function getTitle() { + public function getTitle( $fromMaster = false ) { if ( $this->title ) { return $this->title; } elseif ( $this->pageId ) { @@ -477,7 +478,7 @@ class EchoEvent extends EchoAbstractEntity implements Bundleable { return $this->title = $title; } - return $this->title = Title::newFromID( $this->pageId ); + return $this->title = Title::newFromID( $this->pageId, $fromMaster ? Title::GAID_FOR_UPDATE : 0 ); } elseif ( isset( $this->extra['page_title'], $this->extra['page_namespace'] ) ) { return $this->title = Title::makeTitleSafe( $this->extra['page_namespace'],