mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Echo
synced 2024-12-01 02:46:46 +00:00
bfbfa6be62
Both in the order of the cross-wiki bundles themselves, and in the message in the notification body. ForeignNotifications tracks timestamps per wiki per section, and exposes these through getWikiTimestamp(). ApiEchoNotifications adds these timestamps to the sources manifest, and also sorts the list of wikis by timestamp (it'd be nicer to do this in ForeignPresentationModel instead, but then we'd have to create a new ForeignNotifications instance which causes a duplicated DB query). NotificationsModel receives the timestamp for its wiki as its fallback timestamp, and makes getTimestamp() return this value during the pre-population phase. This causes its parent to automatically sort it correctly. Because the timestamp of a wiki depends on the section (alerts vs messages), we can't put it in the global sources manifest at the top level of the API response. Instead, get rid of this global sources manifest and put all the sources data in the foreign notifications directly. This allows us to specify different timestamps, and also allows us to get rid of code in EchoApi that was already remapping the API response to this format. Bug: T130298 Change-Id: Ie083fbb1ccaf74fbe804633d87ef03c9e71b120f
225 lines
5.8 KiB
PHP
225 lines
5.8 KiB
PHP
<?php
|
|
|
|
class EchoForeignNotifications {
|
|
/**
|
|
* @var bool|EchoUnreadWikis
|
|
*/
|
|
protected $unreadWikis = false;
|
|
|
|
/**
|
|
* @var array [(str) section => (int) count, ...]
|
|
*/
|
|
protected $counts = array( EchoAttributeManager::ALERT => 0, EchoAttributeManager::MESSAGE => 0 );
|
|
|
|
/**
|
|
* @var array [(str) section => (string[]) wikis, ...]
|
|
*/
|
|
protected $wikis = array( EchoAttributeManager::ALERT => array(), EchoAttributeManager::MESSAGE => array() );
|
|
|
|
/**
|
|
* @var array [(str) section => (MWTimestamp) timestamp, ...]
|
|
*/
|
|
protected $timestamps = array( EchoAttributeManager::ALERT => false, EchoAttributeManager::MESSAGE => false );
|
|
|
|
/**
|
|
* @var array [(str) wiki => [ (str) section => (MWTimestamp) timestamp, ...], ...]
|
|
*/
|
|
protected $wikiTimestamps = array();
|
|
|
|
/**
|
|
* @var bool
|
|
*/
|
|
protected $populated = false;
|
|
|
|
/**
|
|
* @param User $user
|
|
*/
|
|
public function __construct( User $user ) {
|
|
if ( $user->getOption( 'echo-cross-wiki-notifications' ) ) {
|
|
$this->unreadWikis = EchoUnreadWikis::newFromUser( $user );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string|null $section Name of section or null for all
|
|
* @return int
|
|
*/
|
|
public function getCount( $section = null ) {
|
|
$this->populate();
|
|
|
|
if ( $section === null ) {
|
|
return array_sum( $this->counts );
|
|
}
|
|
|
|
return isset( $this->counts[$section] ) ? $this->counts[$section] : 0;
|
|
}
|
|
|
|
/**
|
|
* @param string|null $section Name of section or null for all
|
|
* @return MWTimestamp|false
|
|
*/
|
|
public function getTimestamp( $section = null ) {
|
|
$this->populate();
|
|
|
|
if ( $section === null ) {
|
|
$max = false;
|
|
/** @var MWTimestamp $timestamp */
|
|
foreach ( $this->timestamps as $timestamp ) {
|
|
// $timestamp < $max = invert 0
|
|
// $timestamp > $max = invert 1
|
|
if ( $max === false || $timestamp->diff( $max )->invert === 1 ) {
|
|
$max = $timestamp;
|
|
}
|
|
}
|
|
|
|
return $timestamp;
|
|
}
|
|
|
|
return isset( $this->timestamps[$section] ) ? $this->timestamps[$section] : false;
|
|
}
|
|
|
|
/**
|
|
* @param string|null $section Name of section or null for all
|
|
* @return string[]
|
|
*/
|
|
public function getWikis( $section = null ) {
|
|
$this->populate();
|
|
|
|
if ( $section === null ) {
|
|
$all = array();
|
|
foreach ( $this->wikis as $wikis ) {
|
|
$all = array_merge( $all, $wikis );
|
|
}
|
|
|
|
return array_unique( $all );
|
|
}
|
|
|
|
return isset( $this->wikis[$section] ) ? $this->wikis[$section] : array();
|
|
}
|
|
|
|
public function getWikiTimestamp( $wiki, $section = null ) {
|
|
$this->populate();
|
|
if ( !isset( $this->wikiTimestamps[$wiki] ) ) {
|
|
return false;
|
|
}
|
|
if ( $section === null ) {
|
|
$max = false;
|
|
foreach ( $this->wikiTimestamps[$wiki] as $section => $ts ) {
|
|
// $ts < $max = invert 0
|
|
// $ts > $max = invert 1
|
|
if ( $max === false || $ts->diff( $max )->invert === 1 ) {
|
|
$max = $ts;
|
|
}
|
|
}
|
|
return $max;
|
|
}
|
|
return isset( $this->wikiTimestamps[$wiki][$section] ) ? $this->wikiTimestamps[$wiki][$section] : false;
|
|
}
|
|
|
|
protected function populate() {
|
|
if ( $this->populated ) {
|
|
return;
|
|
}
|
|
|
|
if ( $this->unreadWikis === false ) {
|
|
return;
|
|
}
|
|
|
|
$unreadCounts = $this->unreadWikis->getUnreadCounts();
|
|
if ( !$unreadCounts ) {
|
|
return;
|
|
}
|
|
|
|
foreach ( $unreadCounts as $wiki => $sections ) {
|
|
// exclude current wiki
|
|
if ( $wiki === wfWikiID() ) {
|
|
continue;
|
|
}
|
|
|
|
foreach ( $sections as $section => $data ) {
|
|
if ( $data['count'] > 0 ) {
|
|
$this->counts[$section] += $data['count'];
|
|
$this->wikis[$section][] = $wiki;
|
|
|
|
$timestamp = new MWTimestamp( $data['ts'] );
|
|
$this->wikiTimestamps[$wiki][$section] = $timestamp;
|
|
|
|
// We need $this->timestamp[$section] to be the max timestamp
|
|
// across all wikis.
|
|
// $timestamp < $this->timestamps[$section] = invert 0
|
|
// $timestamp > $this->timestamps[$section] = invert 1
|
|
if (
|
|
$this->timestamps[$section] === false ||
|
|
$timestamp->diff( $this->timestamps[$section] )->invert === 1
|
|
) {
|
|
$this->timestamps[$section] = new MWTimestamp( $data['ts'] );
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->populated = true;
|
|
}
|
|
|
|
/**
|
|
* @param string[] $wikis
|
|
* @return array[] [(string) wiki => (array) data]
|
|
*/
|
|
public function getApiEndpoints( array $wikis ) {
|
|
global $wgConf;
|
|
$wgConf->loadFullData();
|
|
|
|
$data = array();
|
|
foreach ( $wikis as $wiki ) {
|
|
$siteFromDB = $wgConf->siteFromDB( $wiki );
|
|
list( $major, $minor ) = $siteFromDB;
|
|
$server = $wgConf->get( 'wgServer', $wiki, $major, array( 'lang' => $minor, 'site' => $major ) );
|
|
$scriptPath = $wgConf->get( 'wgScriptPath', $wiki, $major, array( 'lang' => $minor, 'site' => $major ) );
|
|
|
|
$data[$wiki] = array(
|
|
'title' => $this->getWikiTitle( $wiki, $siteFromDB ),
|
|
'url' => $server . $scriptPath . '/api.php',
|
|
);
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* @param string $wikiId
|
|
* @param array $siteFromDB $wgConf->siteFromDB( $wikiId ) result
|
|
* @return mixed|string
|
|
*/
|
|
protected function getWikiTitle( $wikiId, array $siteFromDB = null ) {
|
|
global $wgConf, $wgLang;
|
|
|
|
$msg = wfMessage( 'project-localized-name-'.$wikiId );
|
|
// check if WikimediaMessages localized project names are available
|
|
if ( $msg->exists() ) {
|
|
return $msg->text();
|
|
} else {
|
|
// don't fetch $site, $langCode if known already
|
|
if ( $siteFromDB === null ) {
|
|
$siteFromDB = $wgConf->siteFromDB( $wikiId );
|
|
}
|
|
list( $site, $langCode ) = $siteFromDB;
|
|
|
|
// try to fetch site name for this specific wiki, or fallback to the
|
|
// general project's sitename if there is no override
|
|
$wikiName = $wgConf->get( 'wgSitename', $wikiId ) ?: $wgConf->get( 'wgSitename', $site );
|
|
$langName = Language::fetchLanguageName( $langCode, $wgLang->getCode() );
|
|
|
|
if ( !$langName ) {
|
|
// if we can't find a language name (in language-agnostic
|
|
// project like mediawikiwiki), including the language name
|
|
// doesn't make much sense
|
|
return $wikiName;
|
|
}
|
|
|
|
// ... or use generic fallback
|
|
return wfMessage( 'echo-foreign-wiki-lang', $wikiName, $langName )->text();
|
|
}
|
|
}
|
|
}
|