2012-04-27 15:14:24 +00:00
|
|
|
<?php
|
|
|
|
|
2016-05-13 20:35:54 +00:00
|
|
|
use MediaWiki\Logger\LoggerFactory;
|
|
|
|
|
2012-04-27 15:14:24 +00:00
|
|
|
class ApiEchoNotifications extends ApiQueryBase {
|
2016-04-21 10:28:32 +00:00
|
|
|
/**
|
|
|
|
* @var EchoForeignNotifications
|
|
|
|
*/
|
|
|
|
protected $foreignNotifications;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
protected $crossWikiSummary = false;
|
2013-12-02 23:11:59 +00:00
|
|
|
|
2012-04-27 15:14:24 +00:00
|
|
|
public function __construct( $query, $moduleName ) {
|
|
|
|
parent::__construct( $query, $moduleName, 'not' );
|
|
|
|
}
|
|
|
|
|
|
|
|
public function execute() {
|
2016-05-13 20:48:03 +00:00
|
|
|
global $wgEchoCrossWikiNotifications;
|
2013-07-12 22:46:37 +00:00
|
|
|
// To avoid API warning, register the parameter used to bust browser cache
|
|
|
|
$this->getMain()->getVal( '_' );
|
|
|
|
|
2016-04-21 11:16:21 +00:00
|
|
|
if ( $this->getUser()->isAnon() ) {
|
2012-12-07 01:08:33 +00:00
|
|
|
$this->dieUsage( 'Login is required', 'login-required' );
|
|
|
|
}
|
2012-06-01 10:57:09 +00:00
|
|
|
|
2012-12-07 01:08:33 +00:00
|
|
|
$params = $this->extractRequestParams();
|
2012-04-27 15:14:24 +00:00
|
|
|
|
2016-01-18 06:27:17 +00:00
|
|
|
/* @deprecated */
|
|
|
|
if ( $params['format'] === 'flyout' ) {
|
|
|
|
$this->setWarning(
|
|
|
|
"notformat=flyout has been deprecated and will be removed soon.\n".
|
|
|
|
"Use notformat=model to get the raw data or notformat=special\n".
|
|
|
|
"for pre-rendered HTML."
|
|
|
|
);
|
|
|
|
} elseif ( $params['format'] === 'html' ) {
|
|
|
|
$this->setWarning(
|
|
|
|
"notformat=html has been deprecated and will be removed soon.\n".
|
|
|
|
"Use notformat=special instead."
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-05-13 20:48:03 +00:00
|
|
|
if ( $wgEchoCrossWikiNotifications ) {
|
|
|
|
$this->foreignNotifications = new EchoForeignNotifications( $this->getUser() );
|
|
|
|
$this->crossWikiSummary = $params['crosswikisummary'];
|
|
|
|
$wikis = $params['wikis'];
|
|
|
|
} else {
|
|
|
|
$wikis = array( wfWikiId() );
|
|
|
|
}
|
2015-11-25 04:07:54 +00:00
|
|
|
|
2016-04-21 11:16:21 +00:00
|
|
|
$results = array();
|
2016-05-13 20:48:03 +00:00
|
|
|
if ( in_array( wfWikiId(), $wikis ) ) {
|
2016-04-21 11:16:21 +00:00
|
|
|
$results[wfWikiId()] = $this->getLocalNotifications( $params );
|
|
|
|
}
|
|
|
|
|
2016-05-13 20:48:03 +00:00
|
|
|
$foreignWikis = array_diff( $wikis, array( wfWikiId() ) );
|
2016-04-21 11:16:21 +00:00
|
|
|
if ( !empty( $foreignWikis ) ) {
|
|
|
|
// get original request params, to forward them to individual wikis
|
|
|
|
$requestParams = $this->getRequest()->getValues();
|
|
|
|
if ( !isset( $requestParams['centralauthtoken'] ) ) {
|
|
|
|
$requestParams['centralauthtoken'] = $this->getCentralAuthToken( $this->getUser() );
|
|
|
|
}
|
|
|
|
$results += $this->getForeignNotifications( $foreignWikis, $requestParams );
|
|
|
|
}
|
|
|
|
|
|
|
|
// after getting local & foreign results, merge them all together
|
|
|
|
$result = $this->mergeResults( $results, $params );
|
|
|
|
if ( $params['groupbysection'] ) {
|
|
|
|
foreach ( $params['sections'] as $section ) {
|
|
|
|
if ( in_array( 'list', $params['prop'] ) ) {
|
|
|
|
$this->getResult()->setIndexedTagName( $result[$section]['list'], 'notification' );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( in_array( 'list', $params['prop'] ) ) {
|
|
|
|
$this->getResult()->setIndexedTagName( $result['list'], 'notification' );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$this->getResult()->addValue( 'query', $this->getModuleName(), $result );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $params
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
protected function getLocalNotifications( array $params ) {
|
|
|
|
$user = $this->getUser();
|
|
|
|
$prop = $params['prop'];
|
|
|
|
|
2012-12-07 01:08:33 +00:00
|
|
|
$result = array();
|
2012-08-30 16:04:39 +00:00
|
|
|
if ( in_array( 'list', $prop ) ) {
|
2014-08-05 21:50:54 +00:00
|
|
|
// Group notification results by section
|
|
|
|
if ( $params['groupbysection'] ) {
|
|
|
|
foreach ( $params['sections'] as $section ) {
|
|
|
|
$result[$section] = $this->getSectionPropList(
|
2015-12-10 16:18:30 +00:00
|
|
|
$user, $section, $params['filter'], $params['limit'],
|
2014-08-14 00:01:35 +00:00
|
|
|
$params[$section . 'continue'], $params['format'], $params[$section . 'unreadfirst']
|
2014-08-05 21:50:54 +00:00
|
|
|
);
|
2015-11-25 04:07:54 +00:00
|
|
|
|
2016-04-21 10:28:32 +00:00
|
|
|
if ( $this->crossWikiSummary && $this->foreignNotifications->getCount( $section ) > 0 ) {
|
2015-11-25 04:07:54 +00:00
|
|
|
// insert fake notification for foreign notifications
|
2016-04-21 10:28:32 +00:00
|
|
|
array_unshift( $result[$section]['list'], $this->makeForeignNotification( $user, $params['format'], $section ) );
|
2015-11-25 04:07:54 +00:00
|
|
|
}
|
2014-08-05 21:50:54 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$attributeManager = EchoAttributeManager::newFromGlobalVars();
|
|
|
|
$result = $this->getPropList(
|
|
|
|
$user,
|
|
|
|
$attributeManager->getUserEnabledEventsbySections( $user, 'web', $params['sections'] ),
|
2016-03-02 01:11:27 +00:00
|
|
|
$params['filter'], $params['limit'], $params['continue'], $params['format'], $params['unreadfirst']
|
2014-08-05 21:50:54 +00:00
|
|
|
);
|
2015-11-25 04:07:54 +00:00
|
|
|
|
2016-04-21 10:28:32 +00:00
|
|
|
// if exactly 1 section is specified, we consider only that section, otherwise
|
2016-04-27 04:18:28 +00:00
|
|
|
// we pass ALL to consider all foreign notifications
|
|
|
|
$section = count( $params['sections'] ) === 1 ? reset( $params['sections'] ) : EchoAttributeManager::ALL;
|
2016-04-21 10:28:32 +00:00
|
|
|
if ( $this->crossWikiSummary && $this->foreignNotifications->getCount( $section ) > 0 ) {
|
|
|
|
array_unshift( $result['list'], $this->makeForeignNotification( $user, $params['format'], $section ) );
|
2015-11-25 04:07:54 +00:00
|
|
|
}
|
2014-07-30 22:09:22 +00:00
|
|
|
}
|
2012-04-27 15:14:24 +00:00
|
|
|
}
|
|
|
|
|
2012-08-30 16:04:39 +00:00
|
|
|
if ( in_array( 'count', $prop ) ) {
|
2014-08-14 18:46:26 +00:00
|
|
|
$result = array_merge_recursive(
|
|
|
|
$result,
|
2016-04-21 10:28:32 +00:00
|
|
|
$this->getPropCount( $user, $params['sections'], $params['groupbysection'] )
|
2014-08-14 18:46:26 +00:00
|
|
|
);
|
2012-04-27 15:14:24 +00:00
|
|
|
}
|
|
|
|
|
2016-04-21 11:16:21 +00:00
|
|
|
return $result;
|
2012-04-27 15:14:24 +00:00
|
|
|
}
|
|
|
|
|
2014-07-30 22:09:22 +00:00
|
|
|
/**
|
2014-08-05 21:50:54 +00:00
|
|
|
* Internal method for getting the property 'list' data for individual section
|
|
|
|
* @param User $user
|
2015-08-15 01:51:11 +00:00
|
|
|
* @param string $section 'alert' or 'message'
|
2015-12-10 16:18:30 +00:00
|
|
|
* @param string $filter 'all', 'read' or 'unread'
|
2014-08-05 21:50:54 +00:00
|
|
|
* @param int $limit
|
|
|
|
* @param string $continue
|
|
|
|
* @param string $format
|
2014-08-14 00:01:35 +00:00
|
|
|
* @param boolean $unreadFirst
|
2014-08-05 21:50:54 +00:00
|
|
|
* @return array
|
|
|
|
*/
|
2015-12-10 16:18:30 +00:00
|
|
|
protected function getSectionPropList( User $user, $section, $filter, $limit, $continue, $format, $unreadFirst = false ) {
|
2014-08-05 21:50:54 +00:00
|
|
|
$attributeManager = EchoAttributeManager::newFromGlobalVars();
|
|
|
|
$sectionEvents = $attributeManager->getUserEnabledEventsbySections( $user, 'web', array( $section ) );
|
2015-08-15 01:51:11 +00:00
|
|
|
|
|
|
|
if ( !$sectionEvents ) {
|
2014-08-05 21:50:54 +00:00
|
|
|
$result = array(
|
|
|
|
'list' => array(),
|
|
|
|
'continue' => null
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$result = $this->getPropList(
|
2015-12-10 16:18:30 +00:00
|
|
|
$user, $sectionEvents, $filter, $limit, $continue, $format, $unreadFirst
|
2014-08-05 21:50:54 +00:00
|
|
|
);
|
|
|
|
}
|
2015-10-01 13:48:52 +00:00
|
|
|
|
2014-07-30 22:09:22 +00:00
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-08-05 21:50:54 +00:00
|
|
|
* Internal helper method for getting property 'list' data, this is based
|
|
|
|
* on the event types specified in the arguments and it could be event types
|
|
|
|
* of a set of sections or a single section
|
|
|
|
* @param User $user
|
2014-08-14 00:01:35 +00:00
|
|
|
* @param string[] $eventTypes
|
2015-12-10 16:18:30 +00:00
|
|
|
* @param string $filter 'all', 'read' or 'unread'
|
2014-08-05 21:50:54 +00:00
|
|
|
* @param int $limit
|
|
|
|
* @param string $continue
|
|
|
|
* @param string $format
|
2014-08-14 00:01:35 +00:00
|
|
|
* @param boolean $unreadFirst
|
2014-08-05 21:50:54 +00:00
|
|
|
* @return array
|
2014-07-30 22:09:22 +00:00
|
|
|
*/
|
2015-12-10 16:18:30 +00:00
|
|
|
protected function getPropList( User $user, array $eventTypes, $filter, $limit, $continue, $format, $unreadFirst = false ) {
|
2014-07-30 22:09:22 +00:00
|
|
|
$result = array(
|
|
|
|
'list' => array(),
|
|
|
|
'continue' => null
|
|
|
|
);
|
|
|
|
|
2014-08-14 18:46:26 +00:00
|
|
|
$notifMapper = new EchoNotificationMapper();
|
2014-08-14 00:01:35 +00:00
|
|
|
|
2015-12-10 16:18:30 +00:00
|
|
|
// check if we want both read & unread...
|
|
|
|
if ( in_array( 'read', $filter ) && in_array( '!read', $filter ) ) {
|
|
|
|
// Prefer unread notifications. We don't care about next offset in this case
|
|
|
|
if ( $unreadFirst ) {
|
|
|
|
// query for unread notifications past 'continue' (offset)
|
|
|
|
$notifs = $notifMapper->fetchUnreadByUser( $user, $limit + 1, $continue, $eventTypes );
|
2015-12-09 18:41:40 +00:00
|
|
|
|
2015-12-10 16:18:30 +00:00
|
|
|
/*
|
|
|
|
* 'continue' has a timestamp & id (to start with, in case
|
|
|
|
* there would be multiple events with that same timestamp)
|
|
|
|
* Unread notifications should always load first, but may be
|
|
|
|
* older than read ones, but we can work with current
|
|
|
|
* 'continue' format:
|
|
|
|
* * if there's no continue, first load unread notifications
|
|
|
|
* * if there's a continue, fetch unread notifications first
|
|
|
|
* * if there are no unread ones, continue must've been
|
|
|
|
* about read notifications: fetch 'em
|
|
|
|
* * if there are unread ones but first one doesn't match
|
|
|
|
* continue id, it must've been about read notifications:
|
|
|
|
* discard unread & fetch read
|
|
|
|
*/
|
|
|
|
if ( $notifs && $continue ) {
|
|
|
|
/** @var EchoNotification $first */
|
|
|
|
$first = reset( $notifs );
|
|
|
|
$continueId = intval( trim( strrchr( $continue, '|' ), '|' ) );
|
|
|
|
if ( $first->getEvent()->getID() !== $continueId ) {
|
|
|
|
// notification doesn't match continue id, it must've been
|
|
|
|
// about read notifications: discard all unread ones
|
|
|
|
$notifs = array();
|
|
|
|
}
|
2015-12-09 18:41:40 +00:00
|
|
|
}
|
|
|
|
|
2015-12-10 16:18:30 +00:00
|
|
|
// If there are less unread notifications than we requested,
|
|
|
|
// then fill the result with some read notifications
|
|
|
|
$count = count( $notifs );
|
|
|
|
// we need 1 more than $limit, so we can respond 'continue'
|
|
|
|
if ( $count <= $limit ) {
|
|
|
|
// Query planner should be smart enough that passing a short list of ids to exclude
|
|
|
|
// will only visit at most that number of extra rows.
|
|
|
|
$mixedNotifs = $notifMapper->fetchByUser(
|
|
|
|
$user,
|
|
|
|
$limit - $count + 1,
|
|
|
|
// if there were unread notifications, 'continue' was for
|
|
|
|
// unread notifications and we should start fetching read
|
|
|
|
// notifications from start
|
|
|
|
$count > 0 ? null : $continue,
|
|
|
|
$eventTypes,
|
|
|
|
array_keys( $notifs )
|
|
|
|
);
|
|
|
|
foreach ( $mixedNotifs as $notif ) {
|
|
|
|
$notifs[$notif->getEvent()->getId()] = $notif;
|
|
|
|
}
|
2014-08-14 00:01:35 +00:00
|
|
|
}
|
2015-12-10 16:18:30 +00:00
|
|
|
} else {
|
|
|
|
$notifs = $notifMapper->fetchByUser( $user, $limit + 1, $continue, $eventTypes );
|
2014-08-14 00:01:35 +00:00
|
|
|
}
|
2015-12-10 16:18:30 +00:00
|
|
|
} elseif ( in_array( 'read', $filter ) ) {
|
|
|
|
$notifs = $notifMapper->fetchReadByUser( $user, $limit + 1, $continue, $eventTypes );
|
|
|
|
} else { // = if ( in_array( '!read', $filter ) ) {
|
|
|
|
$notifs = $notifMapper->fetchUnreadByUser( $user, $limit + 1, $continue, $eventTypes );
|
2014-08-14 00:01:35 +00:00
|
|
|
}
|
2014-08-27 23:59:49 +00:00
|
|
|
|
2014-07-30 22:09:22 +00:00
|
|
|
foreach ( $notifs as $notif ) {
|
2015-10-28 17:15:25 +00:00
|
|
|
$output = EchoDataOutputFormatter::formatOutput( $notif, $format, $user, $this->getLanguage() );
|
|
|
|
if ( $output !== false ) {
|
2016-04-21 09:18:59 +00:00
|
|
|
$result['list'][] = $output;
|
2015-10-28 17:15:25 +00:00
|
|
|
}
|
2014-07-30 22:09:22 +00:00
|
|
|
}
|
|
|
|
|
2014-08-26 20:07:11 +00:00
|
|
|
// Generate offset if necessary
|
2015-12-09 18:41:40 +00:00
|
|
|
if ( count( $result['list'] ) > $limit ) {
|
|
|
|
$lastItem = array_pop( $result['list'] );
|
2016-04-21 11:16:21 +00:00
|
|
|
// @todo: what to do with this when fetching from multiple wikis?
|
2015-12-09 18:41:40 +00:00
|
|
|
$result['continue'] = $lastItem['timestamp']['utcunix'] . '|' . $lastItem['id'];
|
2014-08-26 20:07:11 +00:00
|
|
|
}
|
|
|
|
|
2014-07-30 22:09:22 +00:00
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2014-08-05 21:50:54 +00:00
|
|
|
/**
|
|
|
|
* Internal helper method for getting property 'count' data
|
|
|
|
* @param User $user
|
|
|
|
* @param string[] $sections
|
|
|
|
* @param boolean $groupBySection
|
2015-08-19 20:13:07 +00:00
|
|
|
* @return array
|
2014-08-05 21:50:54 +00:00
|
|
|
*/
|
2016-04-21 10:28:32 +00:00
|
|
|
protected function getPropCount( User $user, array $sections, $groupBySection ) {
|
2014-08-05 21:50:54 +00:00
|
|
|
$result = array();
|
|
|
|
$notifUser = MWEchoNotifUser::newFromUser( $user );
|
NotifUser: Refactor getNotificationCount() and friends, add caching for global counts
Previously, getNotificationCount() only looked at local notifications,
and foreign notifications were added in separately by getMessageCount()
and getAlertCount(). This didn't make any sense and resulted in
counter-intuitive things like I4d49b543.
Instead, add a $global flag to getNotificationCount(). If $global=false,
the local count is returned as before, but if $global=true, the
global count (=local+foreign) is returned. If $global is omitted,
the user's cross-wiki notification preference determines which is returned.
Update getLastUnreadNotificationCount() in the same way, since it had
the same issues.
Also add caching for global counts and timestamps, using a global
memc key.
Bug: T133623
Change-Id: If78bfc710acd91a075771b565cc99f4c302a104d
2016-04-27 07:12:32 +00:00
|
|
|
$global = $this->crossWikiSummary ? 'preference' : false;
|
2014-08-05 21:50:54 +00:00
|
|
|
// Always get total count
|
NotifUser: Refactor getNotificationCount() and friends, add caching for global counts
Previously, getNotificationCount() only looked at local notifications,
and foreign notifications were added in separately by getMessageCount()
and getAlertCount(). This didn't make any sense and resulted in
counter-intuitive things like I4d49b543.
Instead, add a $global flag to getNotificationCount(). If $global=false,
the local count is returned as before, but if $global=true, the
global count (=local+foreign) is returned. If $global is omitted,
the user's cross-wiki notification preference determines which is returned.
Update getLastUnreadNotificationCount() in the same way, since it had
the same issues.
Also add caching for global counts and timestamps, using a global
memc key.
Bug: T133623
Change-Id: If78bfc710acd91a075771b565cc99f4c302a104d
2016-04-27 07:12:32 +00:00
|
|
|
$rawCount = $notifUser->getNotificationCount( true, DB_SLAVE, EchoAttributeManager::ALL, $global );
|
2014-08-05 21:50:54 +00:00
|
|
|
$result['rawcount'] = $rawCount;
|
|
|
|
$result['count'] = EchoNotificationController::formatNotificationCount( $rawCount );
|
|
|
|
|
|
|
|
if ( $groupBySection ) {
|
|
|
|
foreach ( $sections as $section ) {
|
NotifUser: Refactor getNotificationCount() and friends, add caching for global counts
Previously, getNotificationCount() only looked at local notifications,
and foreign notifications were added in separately by getMessageCount()
and getAlertCount(). This didn't make any sense and resulted in
counter-intuitive things like I4d49b543.
Instead, add a $global flag to getNotificationCount(). If $global=false,
the local count is returned as before, but if $global=true, the
global count (=local+foreign) is returned. If $global is omitted,
the user's cross-wiki notification preference determines which is returned.
Update getLastUnreadNotificationCount() in the same way, since it had
the same issues.
Also add caching for global counts and timestamps, using a global
memc key.
Bug: T133623
Change-Id: If78bfc710acd91a075771b565cc99f4c302a104d
2016-04-27 07:12:32 +00:00
|
|
|
$rawCount = $notifUser->getNotificationCount( /* $tryCache = */true, DB_SLAVE, $section, $global );
|
2014-08-05 21:50:54 +00:00
|
|
|
$result[$section]['rawcount'] = $rawCount;
|
|
|
|
$result[$section]['count'] = EchoNotificationController::formatNotificationCount( $rawCount );
|
|
|
|
}
|
|
|
|
}
|
2015-10-01 13:48:52 +00:00
|
|
|
|
2014-08-05 21:50:54 +00:00
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2016-04-27 04:18:28 +00:00
|
|
|
protected function makeForeignNotification( User $user, $format, $section = EchoAttributeManager::ALL ) {
|
2016-04-21 10:28:32 +00:00
|
|
|
$wikis = $this->foreignNotifications->getWikis( $section );
|
|
|
|
$count = $this->foreignNotifications->getCount( $section );
|
2015-11-25 04:07:54 +00:00
|
|
|
|
2016-02-27 05:43:46 +00:00
|
|
|
// Sort wikis by timestamp, in descending order (newest first)
|
2016-04-21 10:28:32 +00:00
|
|
|
usort( $wikis, function ( $a, $b ) use ( $section ) {
|
|
|
|
$aTimestamp = $this->foreignNotifications->getWikiTimestamp( $a, $section ) ?: new MWTimestamp( 0 );
|
|
|
|
$bTimestamp = $this->foreignNotifications->getWikiTimestamp( $b, $section ) ?: new MWTimestamp( 0 );
|
2016-02-27 05:43:46 +00:00
|
|
|
return $bTimestamp->getTimestamp( TS_UNIX ) - $aTimestamp->getTimestamp( TS_UNIX );
|
|
|
|
} );
|
|
|
|
|
2015-11-25 04:07:54 +00:00
|
|
|
$row = new StdClass;
|
|
|
|
$row->event_id = -1;
|
|
|
|
$row->event_type = 'foreign';
|
|
|
|
$row->event_variant = null;
|
|
|
|
$row->event_agent_id = $user->getId();
|
|
|
|
$row->event_agent_ip = null;
|
|
|
|
$row->event_page_id = null;
|
|
|
|
$row->event_page_namespace = null;
|
|
|
|
$row->event_page_title = null;
|
|
|
|
$row->event_extra = serialize( array(
|
2016-04-15 19:45:48 +00:00
|
|
|
'section' => $section ?: 'all',
|
2015-11-25 04:07:54 +00:00
|
|
|
'wikis' => $wikis,
|
|
|
|
'count' => $count
|
|
|
|
) );
|
|
|
|
|
|
|
|
$row->notification_user = $user->getId();
|
2016-04-21 10:28:32 +00:00
|
|
|
$row->notification_timestamp = $this->foreignNotifications->getTimestamp( $section );
|
2015-11-25 04:07:54 +00:00
|
|
|
$row->notification_read_timestamp = null;
|
|
|
|
$row->notification_bundle_base = 1;
|
|
|
|
$row->notification_bundle_hash = md5( 'bogus' );
|
|
|
|
$row->notification_bundle_display_hash = md5( 'also-bogus' );
|
|
|
|
|
2016-02-27 05:43:46 +00:00
|
|
|
// Format output like any other notification
|
2015-11-25 04:07:54 +00:00
|
|
|
$notif = EchoNotification::newFromRow( $row );
|
|
|
|
$output = EchoDataOutputFormatter::formatOutput( $notif, $format, $user, $this->getLanguage() );
|
|
|
|
|
2016-02-27 05:43:46 +00:00
|
|
|
// Add cross-wiki-specific data
|
2016-04-15 19:45:48 +00:00
|
|
|
$output['section'] = $section ?: 'all';
|
2015-11-25 04:07:54 +00:00
|
|
|
$output['count'] = $count;
|
2016-04-27 00:25:14 +00:00
|
|
|
$output['sources'] = EchoForeignNotifications::getApiEndpoints( $wikis );
|
2016-02-27 05:43:46 +00:00
|
|
|
// Add timestamp information
|
|
|
|
foreach ( $output['sources'] as $wiki => &$data ) {
|
2016-04-21 10:28:32 +00:00
|
|
|
$data['ts'] = $this->foreignNotifications->getWikiTimestamp( $wiki, $section )->getTimestamp( TS_MW );
|
2016-02-27 05:43:46 +00:00
|
|
|
}
|
2015-11-25 04:07:54 +00:00
|
|
|
return $output;
|
|
|
|
}
|
|
|
|
|
2016-04-21 11:16:21 +00:00
|
|
|
/**
|
|
|
|
* @param User $user
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function getCentralAuthToken( User $user ) {
|
|
|
|
$context = new RequestContext;
|
|
|
|
$context->setRequest( new FauxRequest( array( 'action' => 'centralauthtoken' ) ) );
|
|
|
|
$context->setUser( $user );
|
|
|
|
|
|
|
|
$api = new ApiMain( $context );
|
|
|
|
$api->execute();
|
|
|
|
|
|
|
|
return $api->getResult()->getResultData( array( 'centralauthtoken', 'centralauthtoken' ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $wikis
|
|
|
|
* @param array $params
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
protected function getForeignNotifications( array $wikis, array $params ) {
|
|
|
|
$apis = $this->foreignNotifications->getApiEndpoints( $wikis );
|
|
|
|
if ( !$apis ) {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
2016-05-04 00:46:59 +00:00
|
|
|
// Don't request cross-wiki notifications
|
|
|
|
unset( $params['notcrosswikisummary'] );
|
2016-05-13 23:28:30 +00:00
|
|
|
$params['format'] = 'json';
|
2016-04-21 11:16:21 +00:00
|
|
|
|
2016-05-04 00:46:59 +00:00
|
|
|
$reqs = array();
|
2016-04-21 11:16:21 +00:00
|
|
|
foreach ( $apis as $wiki => $api ) {
|
2016-05-04 00:46:59 +00:00
|
|
|
$reqs[$wiki] = array(
|
|
|
|
'method' => 'GET',
|
|
|
|
'url' => $api['url'],
|
|
|
|
// Only request data from that specific wiki, or they'd all spawn
|
|
|
|
// cross-wiki api requests...
|
|
|
|
'query' => array_merge( $params, array( 'notwikis' => $wiki ) ),
|
|
|
|
);
|
2016-04-21 11:16:21 +00:00
|
|
|
}
|
|
|
|
|
2016-05-04 00:46:59 +00:00
|
|
|
$http = new MultiHttpClient( array() );
|
|
|
|
$responses = $http->runMulti( $reqs );
|
2016-04-21 11:16:21 +00:00
|
|
|
|
2016-05-05 19:58:28 +00:00
|
|
|
$results = array();
|
2016-05-04 00:46:59 +00:00
|
|
|
foreach ( $responses as $wiki => $response ) {
|
|
|
|
$statusCode = $response['response']['code'];
|
|
|
|
if ( $statusCode >= 200 && $statusCode <= 299 ) {
|
2016-05-13 23:28:30 +00:00
|
|
|
$parsed = json_decode( $response['response']['body'], true );
|
2016-05-13 00:55:51 +00:00
|
|
|
if ( $parsed && isset( $parsed['query']['notifications'] ) ) {
|
2016-05-04 00:46:59 +00:00
|
|
|
$results[$wiki] = $parsed['query']['notifications'];
|
|
|
|
}
|
2016-05-13 20:35:54 +00:00
|
|
|
} else {
|
|
|
|
LoggerFactory::getInstance( 'Echo' )->warning(
|
|
|
|
"Failed to fetch notifications from {wiki}. Response: {response}",
|
|
|
|
array(
|
|
|
|
'wiki' => $wiki,
|
|
|
|
'response' => $response['response']['body'],
|
|
|
|
)
|
|
|
|
);
|
2016-04-21 11:16:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $results
|
|
|
|
* @param array $params
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
protected function mergeResults( array $results, array $params ) {
|
|
|
|
$master = array_shift( $results );
|
2016-05-09 18:08:00 +00:00
|
|
|
if ( !$master ) {
|
|
|
|
$master = array();
|
|
|
|
}
|
2016-04-21 11:16:21 +00:00
|
|
|
|
|
|
|
if ( in_array( 'list', $params['prop'] ) ) {
|
|
|
|
$master = $this->mergeList( $master, $results, $params['groupbysection'] );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( in_array( 'count', $params['prop'] ) && !$this->crossWikiSummary ) {
|
|
|
|
// if crosswiki data was requested, the count in $master
|
|
|
|
// is accurate already
|
|
|
|
// otherwise, we'll want to combine counts for all wikis
|
|
|
|
$master = $this->mergeCount( $master, $results, $params['groupbysection'] );
|
|
|
|
}
|
|
|
|
|
|
|
|
return $master;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $master
|
|
|
|
* @param array $results
|
|
|
|
* @param bool $groupBySection
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
protected function mergeList( array $master, array $results, $groupBySection ) {
|
|
|
|
// sort all notifications by timestamp: most recent first
|
|
|
|
$sort = function( $a, $b ) {
|
|
|
|
return $a['timestamp']['utcunix'] - $b['timestamp']['utcunix'];
|
|
|
|
};
|
|
|
|
|
|
|
|
if ( $groupBySection ) {
|
|
|
|
foreach ( EchoAttributeManager::$sections as $section ) {
|
2016-05-09 18:08:00 +00:00
|
|
|
if ( !isset( $master[$section]['list'] ) ) {
|
|
|
|
$master[$section]['list'] = array();
|
|
|
|
}
|
2016-04-21 11:16:21 +00:00
|
|
|
foreach ( $results as $result ) {
|
|
|
|
$master[$section]['list'] = array_merge( $master[$section]['list'], $result[$section]['list'] );
|
|
|
|
}
|
|
|
|
usort( $master[$section]['list'], $sort );
|
|
|
|
}
|
|
|
|
} else {
|
2016-05-13 00:55:51 +00:00
|
|
|
if ( !isset( $master['list'] ) || !is_array( $master['list'] ) ) {
|
2016-05-09 18:08:00 +00:00
|
|
|
$master['list'] = array();
|
|
|
|
}
|
2016-04-21 11:16:21 +00:00
|
|
|
foreach ( $results as $result ) {
|
|
|
|
$master['list'] = array_merge( $master['list'], $result['list'] );
|
|
|
|
}
|
|
|
|
usort( $master['list'], $sort );
|
|
|
|
}
|
|
|
|
|
|
|
|
return $master;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $master
|
|
|
|
* @param array $results
|
|
|
|
* @param bool $groupBySection
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
protected function mergeCount( array $master, array $results, $groupBySection ) {
|
|
|
|
if ( $groupBySection ) {
|
|
|
|
foreach ( EchoAttributeManager::$sections as $section ) {
|
2016-05-09 18:08:00 +00:00
|
|
|
if ( !isset( $master[$section]['rawcount'] ) ) {
|
|
|
|
$master[$section]['rawcount'] = 0;
|
|
|
|
}
|
2016-04-21 11:16:21 +00:00
|
|
|
foreach ( $results as $result ) {
|
|
|
|
$master[$section]['rawcount'] += $result[$section]['rawcount'];
|
|
|
|
}
|
|
|
|
$master[$section]['count'] = EchoNotificationController::formatNotificationCount( $master[$section]['rawcount'] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-09 18:08:00 +00:00
|
|
|
if ( !isset( $master['rawcount'] ) ) {
|
|
|
|
$master['rawcount'] = 0;
|
|
|
|
}
|
2016-04-21 11:16:21 +00:00
|
|
|
foreach ( $results as $result ) {
|
|
|
|
// regardless of groupbysection, totals are always included
|
|
|
|
$master['rawcount'] += $result['rawcount'];
|
|
|
|
}
|
|
|
|
$master['count'] = EchoNotificationController::formatNotificationCount( $master['rawcount'] );
|
|
|
|
|
|
|
|
return $master;
|
|
|
|
}
|
|
|
|
|
2012-04-27 15:14:24 +00:00
|
|
|
public function getAllowedParams() {
|
2016-05-13 20:48:03 +00:00
|
|
|
global $wgConf, $wgEchoCrossWikiNotifications;
|
2016-04-21 11:16:21 +00:00
|
|
|
|
2014-08-05 21:50:54 +00:00
|
|
|
$sections = EchoAttributeManager::$sections;
|
|
|
|
$params = array(
|
2015-12-10 16:18:30 +00:00
|
|
|
'filter' => array(
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
ApiBase::PARAM_DFLT => 'read|!read',
|
|
|
|
ApiBase::PARAM_TYPE => array(
|
|
|
|
'read',
|
|
|
|
'!read',
|
|
|
|
),
|
|
|
|
),
|
2012-04-27 15:14:24 +00:00
|
|
|
'prop' => array(
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
ApiBase::PARAM_TYPE => array(
|
2012-08-31 21:50:46 +00:00
|
|
|
'list',
|
|
|
|
'count',
|
|
|
|
),
|
2012-04-27 15:14:24 +00:00
|
|
|
ApiBase::PARAM_DFLT => 'list',
|
|
|
|
),
|
2014-08-05 21:50:54 +00:00
|
|
|
'sections' => array(
|
|
|
|
ApiBase::PARAM_DFLT => implode( '|', $sections ),
|
|
|
|
ApiBase::PARAM_TYPE => $sections,
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
),
|
2014-08-14 00:01:35 +00:00
|
|
|
'groupbysection' => array(
|
|
|
|
ApiBase::PARAM_TYPE => 'boolean',
|
|
|
|
ApiBase::PARAM_DFLT => false,
|
|
|
|
),
|
2012-04-27 15:14:24 +00:00
|
|
|
'format' => array(
|
|
|
|
ApiBase::PARAM_TYPE => array(
|
2012-08-31 21:50:46 +00:00
|
|
|
'text',
|
2015-11-16 16:05:07 +00:00
|
|
|
'model',
|
2015-11-11 02:23:03 +00:00
|
|
|
'special',
|
2016-01-18 06:27:17 +00:00
|
|
|
'flyout', /* @deprecated */
|
|
|
|
'html', /* @deprecated */
|
2012-08-31 21:50:46 +00:00
|
|
|
),
|
2016-01-18 06:27:17 +00:00
|
|
|
ApiBase::PARAM_HELP_MSG_PER_VALUE => array(),
|
2012-04-27 15:14:24 +00:00
|
|
|
),
|
2012-08-01 19:53:05 +00:00
|
|
|
'limit' => array(
|
2013-05-02 17:56:46 +00:00
|
|
|
ApiBase::PARAM_TYPE => 'limit',
|
2012-08-01 19:53:05 +00:00
|
|
|
ApiBase::PARAM_DFLT => 20,
|
|
|
|
ApiBase::PARAM_MIN => 1,
|
2013-05-02 17:56:46 +00:00
|
|
|
ApiBase::PARAM_MAX => ApiBase::LIMIT_SML1,
|
|
|
|
ApiBase::PARAM_MAX2 => ApiBase::LIMIT_SML2,
|
2012-08-01 19:53:05 +00:00
|
|
|
),
|
2014-10-28 19:42:41 +00:00
|
|
|
'continue' => array(
|
2015-10-26 15:41:57 +00:00
|
|
|
ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
|
2014-10-28 19:42:41 +00:00
|
|
|
),
|
2016-03-02 01:11:27 +00:00
|
|
|
'unreadfirst' => array(
|
|
|
|
ApiBase::PARAM_TYPE => 'boolean',
|
|
|
|
ApiBase::PARAM_DFLT => false,
|
|
|
|
),
|
2012-04-27 15:14:24 +00:00
|
|
|
);
|
2014-08-05 21:50:54 +00:00
|
|
|
foreach ( $sections as $section ) {
|
|
|
|
$params[$section . 'continue'] = null;
|
2014-08-14 00:01:35 +00:00
|
|
|
$params[$section . 'unreadfirst'] = array(
|
|
|
|
ApiBase::PARAM_TYPE => 'boolean',
|
|
|
|
ApiBase::PARAM_DFLT => false,
|
|
|
|
);
|
2014-08-05 21:50:54 +00:00
|
|
|
}
|
2015-10-01 13:48:52 +00:00
|
|
|
|
2016-05-13 20:48:03 +00:00
|
|
|
if ( $wgEchoCrossWikiNotifications ) {
|
|
|
|
$params += array(
|
|
|
|
// fetch notifications from multiple wikis
|
|
|
|
'wikis' => array(
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
ApiBase::PARAM_DFLT => wfWikiId(),
|
|
|
|
ApiBase::PARAM_TYPE => array_unique( array_merge( $wgConf->wikis, array( wfWikiId() ) ) ),
|
|
|
|
),
|
|
|
|
// create "x notifications from y wikis" notification bundle &
|
|
|
|
// include unread counts from other wikis in prop=count results
|
|
|
|
'crosswikisummary' => array(
|
|
|
|
ApiBase::PARAM_TYPE => 'boolean',
|
|
|
|
ApiBase::PARAM_DFLT => false,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2014-08-05 21:50:54 +00:00
|
|
|
return $params;
|
2012-04-27 15:14:24 +00:00
|
|
|
}
|
|
|
|
|
2014-10-28 19:42:41 +00:00
|
|
|
/**
|
|
|
|
* @see ApiBase::getExamplesMessages()
|
|
|
|
*/
|
|
|
|
protected function getExamplesMessages() {
|
|
|
|
return array(
|
|
|
|
'action=query&meta=notifications'
|
|
|
|
=> 'apihelp-query+notifications-example-1',
|
|
|
|
'action=query&meta=notifications¬prop=count¬sections=alert|message¬groupbysection=1'
|
|
|
|
=> 'apihelp-query+notifications-example-2',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2012-04-27 15:14:24 +00:00
|
|
|
public function getHelpUrls() {
|
2014-08-14 21:33:23 +00:00
|
|
|
return 'https://www.mediawiki.org/wiki/Echo_(Notifications)/API';
|
2012-04-27 15:14:24 +00:00
|
|
|
}
|
2012-07-26 17:23:18 +00:00
|
|
|
}
|