Merge "Moderate notifications"

This commit is contained in:
jenkins-bot 2016-08-19 22:34:16 +00:00 committed by Gerrit Code Review
commit 95f829a3b7
10 changed files with 151 additions and 74 deletions

View file

@ -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';

View file

@ -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;
}
}

View file

@ -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',

View file

@ -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;

View file

@ -0,0 +1,72 @@
<?php
use MediaWiki\Logger\LoggerFactory;
/**
* Mark event notifications as deleted at the end of a request. Used to queue up
* individual events to mark due to formatting failures.
*/
class EchoDeferredMarkAsDeletedUpdate implements DeferrableUpdate {
/**
* @var array
*/
protected $events = array();
/**
* @param EchoEvent $event
*/
public static function add( EchoEvent $event ) {
static $update;
if ( $update === null ) {
$update = new self();
DeferredUpdates::addUpdate( $update );
}
$update->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();
}
}

View file

@ -1,52 +0,0 @@
<?php
/**
* Mark event notifications as read at the end of a request. Used to queue up
* individual events to mark due to formatting failures or other uses.
*/
class EchoDeferredMarkAsReadUpdate implements DeferrableUpdate {
/**
* @var array
*/
protected $events = array();
/**
* @param EchoEvent $event
* @param User $user
*/
public static function add( EchoEvent $event, User $user ) {
static $update;
if ( $update === null ) {
$update = new self();
DeferredUpdates::addUpdate( $update );
}
$update->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();
}
}

View file

@ -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.
*

View file

@ -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
*

View file

@ -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
),

View file

@ -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'],