Implement subject+talk and null+user page grouping

Make it optional through the unpgrouppages parameter, so that
generic usage of the unreadnotificationpages API is still possible.

In the front-end, store which display title maps to what set of titles,
and pass in the full set rather than just the display title when
filtering by a page.

Bug: T137502
Change-Id: I443ca00ff5e5d36fd6910101226358942e6aa8ee
This commit is contained in:
Roan Kattouw 2016-06-23 00:14:48 +02:00 committed by Moriel Schottlender
parent 9e206ef8f5
commit 4e64643eb7
7 changed files with 118 additions and 28 deletions

View file

@ -261,6 +261,7 @@
"apihelp-query+notifications-example-1": "List notifications", "apihelp-query+notifications-example-1": "List notifications",
"apihelp-query+notifications-example-2": "List notifications, grouped by section, with counts", "apihelp-query+notifications-example-2": "List notifications, grouped by section, with counts",
"apihelp-query+unreadnotificationpages-description": "Get pages for which there are unread notifications for the current user.", "apihelp-query+unreadnotificationpages-description": "Get pages for which there are unread notifications for the current user.",
"apihelp-query+unreadnotificationpages-param-grouppages": "Group talk pages together with their subject page, and group notifications not associated with a page together with the current user's user page.",
"apihelp-query+unreadnotificationpages-param-limit": "The maximum number of pages to return.", "apihelp-query+unreadnotificationpages-param-limit": "The maximum number of pages to return.",
"apihelp-query+unreadnotificationpages-param-wikis": "List of wikis to fetch pages with unread notifications from (defaults to only current wiki).", "apihelp-query+unreadnotificationpages-param-wikis": "List of wikis to fetch pages with unread notifications from (defaults to only current wiki).",
"apihelp-query+unreadnotificationpages-example-1": "List pages with (their amount of) unread notifications" "apihelp-query+unreadnotificationpages-example-1": "List pages with (their amount of) unread notifications"

View file

@ -253,6 +253,7 @@
"apihelp-query+notifications-example-1": "{{doc-apihelp-example|query+notifications}}", "apihelp-query+notifications-example-1": "{{doc-apihelp-example|query+notifications}}",
"apihelp-query+notifications-example-2": "{{doc-apihelp-example|query+notifications}}", "apihelp-query+notifications-example-2": "{{doc-apihelp-example|query+notifications}}",
"apihelp-query+unreadnotificationpages-description": "{{doc-apihelp-description|query+unreadnotificationpages}}", "apihelp-query+unreadnotificationpages-description": "{{doc-apihelp-description|query+unreadnotificationpages}}",
"apihelp-query+unreadnotificationpages-param-grouppages": "{{doc-apihelp-param|query+unreadnotificationpages|grouppages}}",
"apihelp-query+unreadnotificationpages-param-limit": "{{doc-apihelp-param|query+unreadnotificationpages|limit}}", "apihelp-query+unreadnotificationpages-param-limit": "{{doc-apihelp-param|query+unreadnotificationpages|limit}}",
"apihelp-query+unreadnotificationpages-param-wikis": "{{doc-apihelp-param|query+unreadnotificationpages|wikis}}", "apihelp-query+unreadnotificationpages-param-wikis": "{{doc-apihelp-param|query+unreadnotificationpages|wikis}}",
"apihelp-query+unreadnotificationpages-example-1": "{{doc-apihelp-example|query+unreadnotificationpages}}" "apihelp-query+unreadnotificationpages-example-1": "{{doc-apihelp-example|query+unreadnotificationpages}}"

View file

@ -28,8 +28,8 @@ class ApiEchoUnreadNotificationPages extends ApiCrossWikiBase {
$params = $this->extractRequestParams(); $params = $this->extractRequestParams();
$result = array(); $result = array();
if ( in_array( wfWikiID(), $this->getRequestedWikis() ) ) { if ( in_array( wfWikiId(), $this->getRequestedWikis() ) ) {
$result[wfWikiID()] = $this->getFromLocal( $params['limit'] ); $result[wfWikiID()] = $this->getFromLocal( $params['limit'], $params['grouppages'] );
} }
if ( $this->getRequestedForeignWikis() ) { if ( $this->getRequestedForeignWikis() ) {
@ -47,10 +47,14 @@ class ApiEchoUnreadNotificationPages extends ApiCrossWikiBase {
/** /**
* @param int $limit * @param int $limit
* @param bool $groupPages
* @return array * @return array
*/ */
protected function getFromLocal( $limit ) { protected function getFromLocal( $limit, $groupPages ) {
$dbr = MWEchoDbFactory::newFromDefault()->getEchoDb( DB_SLAVE ); $dbr = MWEchoDbFactory::newFromDefault()->getEchoDb( DB_SLAVE );
// If $groupPages is true, we need to fetch all pages and apply the ORDER BY and LIMIT ourselves
// after grouping.
$extraOptions = $groupPages ? array() : array( 'ORDER BY' => 'count DESC', 'LIMIT' => $limit );
$rows = $dbr->select( $rows = $dbr->select(
array( 'echo_event', 'echo_notification' ), array( 'echo_event', 'echo_notification' ),
array( 'event_page_id', 'count' => 'COUNT(*)' ), array( 'event_page_id', 'count' => 'COUNT(*)' ),
@ -63,9 +67,7 @@ class ApiEchoUnreadNotificationPages extends ApiCrossWikiBase {
__METHOD__, __METHOD__,
array( array(
'GROUP BY' => 'event_page_id', 'GROUP BY' => 'event_page_id',
'ORDER BY' => 'count DESC', ) + $extraOptions,
'LIMIT' => $limit,
),
array( 'echo_notification' => array( 'INNER JOIN', 'notification_event = event_id' ) ) array( 'echo_notification' => array( 'INNER JOIN', 'notification_event = event_id' ) )
); );
@ -73,17 +75,75 @@ class ApiEchoUnreadNotificationPages extends ApiCrossWikiBase {
return array(); return array();
} }
$pages = array(); $nullCount = 0;
$pageCounts = array();
foreach ( $rows as $row ) { foreach ( $rows as $row ) {
$pages[$row->event_page_id] = $row->count; if ( $row->event_page_id !== null ) {
$pageCounts[$row->event_page_id] = intval( $row->count );
} else {
$nullCount = intval( $row->count );
}
}
$titles = Title::newFromIDs( array_keys( $pageCounts ) );
$groupCounts = array();
foreach ( $titles as $title ) {
if ( $groupPages ) {
// If $title is a talk page, add its count to its subject page's count
$pageName = $title->getSubjectPage()->getPrefixedText();
} else {
$pageName = $title->getPrefixedText();
}
$count = $pageCounts[$title->getArticleId()];
if ( isset( $groupCounts[$pageName] ) ) {
$groupCounts[$pageName] += $count;
} else {
$groupCounts[$pageName] = $count;
}
}
$userPageName = $this->getUser()->getUserPage()->getPrefixedText();
if ( $nullCount > 0 && $groupPages ) {
// Add the count for NULL (not associated with any page) to the count for the user page
if ( isset( $groupCounts[$userPageName] ) ) {
$groupCounts[$userPageName] += $nullCount;
} else {
$groupCounts[$userPageName] = $nullCount;
}
}
arsort( $groupCounts );
if ( $groupPages ) {
$groupCounts = array_slice( $groupCounts, 0, $limit );
} }
$result = array(); $result = array();
$titles = Title::newFromIDs( array_keys( $pages ) ); foreach ( $groupCounts as $pageName => $count ) {
foreach ( $titles as $title ) { if ( $groupPages ) {
$result[] = array( $title = Title::newFromText( $pageName );
$pages = array( $title->getSubjectPage()->getPrefixedText(), $title->getTalkPage()->getPrefixedText() );
if ( $pageName === $userPageName ) {
$pages[] = null;
}
$pageDescription = array(
'ns' => $title->getNamespace(),
'title' => $title->getPrefixedText(), 'title' => $title->getPrefixedText(),
'count' => $pages[$title->getArticleID()], 'unprefixed' => $title->getText(),
'pages' => $pages,
);
} else {
$pageDescription = array( 'title' => $pageName );
}
$result[] = $pageDescription + array(
'count' => $count,
);
}
if ( !$groupPages && $nullCount > 0 ) {
$result[] = array(
'title' => null,
'count' => $nullCount,
); );
} }
@ -112,9 +172,13 @@ class ApiEchoUnreadNotificationPages extends ApiCrossWikiBase {
global $wgEchoMaxUpdateCount; global $wgEchoMaxUpdateCount;
return parent::getAllowedParams() + array( return parent::getAllowedParams() + array(
'grouppages' => array(
ApiBase::PARAM_TYPE => 'boolean',
ApiBase::PARAM_DFLT => false,
),
'limit' => array( 'limit' => array(
ApiBase::PARAM_TYPE => 'limit', ApiBase::PARAM_TYPE => 'limit',
ApiBase::PARAM_DFLT => 20, ApiBase::PARAM_DFLT => 10,
ApiBase::PARAM_MIN => 1, ApiBase::PARAM_MIN => 1,
ApiBase::PARAM_MAX => $wgEchoMaxUpdateCount, ApiBase::PARAM_MAX => $wgEchoMaxUpdateCount,
ApiBase::PARAM_MAX2 => $wgEchoMaxUpdateCount, ApiBase::PARAM_MAX2 => $wgEchoMaxUpdateCount,

View file

@ -68,7 +68,8 @@
var params = { var params = {
action: 'query', action: 'query',
meta: 'unreadnotificationpages', meta: 'unreadnotificationpages',
uselang: this.userLang uselang: this.userLang,
unpgrouppages: true
}; };
if ( !sources || sources === '*' ) { if ( !sources || sources === '*' ) {

View file

@ -132,7 +132,7 @@
{ {
continue: continueValue, continue: continueValue,
readState: filters.getReadState(), readState: filters.getReadState(),
titles: filters.getSourcePagesModel().getCurrentPage() titles: filters.getSourcePagesModel().getGroupedPagesForCurrentTitle()
} }
) )
.then( function ( data ) { .then( function ( data ) {

View file

@ -123,12 +123,32 @@
* Get all pages in a source * Get all pages in a source
* *
* @param {string} source Symbolic name of the source * @param {string} source Symbolic name of the source
* @return {Object[]} Page definitions in this source * @return {Object} Page definitions in this source
*/ */
mw.echo.dm.SourcePagesModel.prototype.getSourcePages = function ( source ) { mw.echo.dm.SourcePagesModel.prototype.getSourcePages = function ( source ) {
return this.sources[ source ] && this.sources[ source ].pages; return this.sources[ source ] && this.sources[ source ].pages;
}; };
/**
* Get the list of page titles associated with one group title.
*
* @param {string} source Symbolic name of the source
* @param {string} title Group title
* @return {string[]} Page titles
*/
mw.echo.dm.SourcePagesModel.prototype.getGroupedPagesForTitle = function ( source, title ) {
return OO.getProp( this.sources, source, 'pages', title, 'pages' ) || [];
};
/**
* Get the list of page titles associated with the current group title.
*
* @return {string[]} Page titles
*/
mw.echo.dm.SourcePagesModel.prototype.getGroupedPagesForCurrentTitle = function () {
return this.getGroupedPagesForTitle( this.getCurrentSource(), this.getCurrentPage() );
};
/** /**
* Reset the data * Reset the data
*/ */
@ -144,11 +164,17 @@
* @param {Object} details Details object * @param {Object} details Details object
*/ */
mw.echo.dm.SourcePagesModel.prototype.setSourcePagesDetails = function ( source, details ) { mw.echo.dm.SourcePagesModel.prototype.setSourcePagesDetails = function ( source, details ) {
var i, page;
this.sources[ source ] = { this.sources[ source ] = {
title: details.source.title, title: details.source.title,
base: details.source.base, base: details.source.base,
totalCount: details.totalCount, totalCount: details.totalCount,
pages: details.pages pages: {}
}; };
for ( i = 0; i < details.pages.length; i++ ) {
page = details.pages[ i ];
this.sources[ source ].pages[ page.title ] = page;
}
}; };
} )( mediaWiki ); } )( mediaWiki );

View file

@ -72,21 +72,18 @@
* Populate the widget from the model * Populate the widget from the model
*/ */
mw.echo.ui.PageFilterWidget.prototype.populateDataFromModel = function () { mw.echo.ui.PageFilterWidget.prototype.populateDataFromModel = function () {
var i, title, widget, var title, widget, isUserPage,
optionWidgets = [], optionWidgets = [],
sourcePages = this.model.getSourcePages( this.source ); sourcePages = this.model.getSourcePages( this.source );
for ( i = 0; i < sourcePages.length; i++ ) { for ( title in sourcePages ) {
isUserPage = sourcePages[ title ].ns === mw.config.get( 'wgNamespaceIds' ).user;
widget = new mw.echo.ui.PageNotificationsOptionWidget( { widget = new mw.echo.ui.PageNotificationsOptionWidget( {
label: sourcePages[ i ].title, label: isUserPage ? sourcePages[ title ].unprefixed : title,
title: sourcePages[ i ].title, title: isUserPage ? sourcePages[ title ].unprefixed : title,
// TODO: Pages that are a user page should icon: isUserPage ? 'userAvatar' : 'article',
// have a user icon unreadCount: sourcePages[ title ].count,
icon: 'article', data: title,
unreadCount: sourcePages[ i ].count,
// TODO: When we group pages, this should be
// an array of titles
data: sourcePages[ i ].title,
classes: [ 'mw-echo-ui-pageFilterWidget-page' ] classes: [ 'mw-echo-ui-pageFilterWidget-page' ]
} ); } );
optionWidgets.push( widget ); optionWidgets.push( widget );