mediawiki-extensions-Echo/modules/api/mw.echo.api.APIHandler.js
Stephane Bisson f5f59a503b Use wikiId both server and client side
When creating the various notification sources,
the server was indexing them with wfWikiID() but
the client was using wgDBname to find which one
is local and which are remote. On some wikis,
like TWN, wfWikiID() includes a db suffix so
the JS app on Special:Notifications is failing to
find the local source and errors.

Bug: T167336
Change-Id: Id60f723b615fb7db54a6f17b1c1be20dfe98e36c
2019-05-08 09:03:28 -04:00

276 lines
8.7 KiB
JavaScript

( function () {
/**
* Abstract notification API handler
*
* @abstract
* @class
*
* @constructor
* @param {mw.Api} api
* @param {Object} [config] Configuration object
* @cfg {number} [limit=25] The limit on how many notifications to fetch
* @cfg {string} [userLang=mw.config.get( 'wgUserLanguage' )] User language. Defaults
* to the default user language configuration settings.
*/
mw.echo.api.APIHandler = function MwEchoApiAPIHandler( api, config ) {
config = config || {};
this.fetchNotificationsPromise = {};
this.apiErrorState = {};
this.limit = config.limit || 25;
this.userLang = config.userLang || mw.config.get( 'wgUserLanguage' );
this.api = api;
// Map the logical type to the type
// that the API recognizes
this.normalizedType = {
message: 'message',
alert: 'alert',
all: 'message|alert'
};
// Parameters that are sent through
// to the 'fetch notification' promise
// per type
this.typeParams = {
message: {},
alert: {},
all: {}
};
};
/* Setup */
OO.initClass( mw.echo.api.APIHandler );
/**
* Fetch notifications from the API.
*
* @param {string} type Notification type
* @param {Object} [overrideParams] An object defining parameters to override in the API
* fetching call.
* @return {jQuery.Promise} A promise that resolves with an object containing the
* notification items
*/
mw.echo.api.APIHandler.prototype.fetchNotifications = null;
/**
* Send a general query to the API. This is mostly for dynamic actions
* where other extensions may set up API actions that are unique and
* unanticipated.
*
* @param {Object} data Data object about the operation.
* @param {string} [data.tokenType=csrf] Token type, 'csrf', 'watch', etc
* @param {Object} [data.params] Parameters to pass to the API call
* @return {jQuery.Promise} Promise that is resolved when the action
* is complete
*/
mw.echo.api.APIHandler.prototype.queryAPI = function ( data ) {
return this.api.postWithToken( data.tokenType || 'csrf', data.params );
};
/**
* Fetch all pages with unread notifications in them per wiki
*
* @param {string|string[]} [sources=*] Requested sources. If not given
* or if a '*' is given, all available sources will be queried
* @return {jQuery.Promise} Promise that is resolved with an object
* of pages with the number of unread notifications per wiki
*/
mw.echo.api.APIHandler.prototype.fetchUnreadNotificationPages = function ( sources ) {
var params = {
action: 'query',
meta: 'unreadnotificationpages',
uselang: this.userLang,
unpgrouppages: true
};
if ( !sources || sources === '*' ) {
params.unpwikis = '*';
} else {
sources = Array.isArray( sources ) ? sources : [ sources ];
params.unpwikis = sources.join( '|' );
}
return this.api.get( params );
};
/**
* Check if the given source is local
*
* @param {string|string[]} sources Source names
* @return {boolean} Source is local
*/
mw.echo.api.APIHandler.prototype.isSourceLocal = function ( sources ) {
return Array.isArray( sources ) ?
(
sources.indexOf( 'local' ) !== -1 ||
sources.indexOf( mw.config.get( 'wgWikiId' ) ) !== -1
) :
(
sources === 'local' ||
sources === mw.config.get( 'wgWikiId' )
);
};
/**
* Create a new fetchNotifications promise that queries the API and overrides
* the cached promise.
*
* @param {string} type Notification type
* @param {string[]} [sources] An array of sources to query
* @param {Object} [overrideParams] An object defining parameters to override in the API
* fetching call.
* @return {jQuery.Promise} Promise that is resolved when notifications are
* fetched from the API.
*/
mw.echo.api.APIHandler.prototype.createNewFetchNotificationPromise = function ( type, sources, overrideParams ) {
var fetchNotifPromise,
fetchingSource = 'local',
me = this,
params = $.extend( {
action: 'query',
formatversion: 2,
meta: 'notifications',
notsections: this.normalizedType[ type ],
notformat: 'model',
notlimit: this.limit,
notprop: 'list|count|seenTime',
uselang: this.userLang
}, this.getTypeParams( type ) );
if ( !this.isSourceLocal( sources ) ) {
params.notwikis = sources.join( '|' );
params.notfilter = '!read';
fetchingSource = 'foreign';
}
// Initialize the nested value if it doesn't yet exist
this.fetchNotificationsPromise[ type ] = this.fetchNotificationsPromise[ type ] || {};
me.apiErrorState[ type ] = me.apiErrorState[ type ] || {};
// Reset cached values
this.fetchNotificationsPromise[ type ][ fetchingSource ] = null;
this.apiErrorState[ type ][ fetchingSource ] = false;
// Create the fetch promise
fetchNotifPromise = this.api.get( $.extend( true, params, overrideParams ) );
// Only cache promises that don't have override params in them
if ( !overrideParams ) {
this.fetchNotificationsPromise[ type ][ fetchingSource ] = fetchNotifPromise;
}
return fetchNotifPromise
.fail( function () {
// Mark API error state
me.apiErrorState[ type ][ fetchingSource ] = true;
} );
};
/**
* Update the seen timestamp
*
* @param {string|string[]} [types] Notification type 'message', 'alert' or [ 'message', 'alert' ].
* @return {jQuery.Promise} A promise that resolves with the seen timestamp, as a full UTC
* ISO 8601 timestamp.
*/
mw.echo.api.APIHandler.prototype.updateSeenTime = null;
/**
* Mark all notifications as read
*
* @param {string} source Wiki name
* @param {string|string[]} type Notification type 'message', 'alert' or 'all'.
* @return {jQuery.Promise} A promise that resolves when all notifications
* are marked as read.
*/
mw.echo.api.APIHandler.prototype.markAllRead = null;
/**
* Mark multiple notification items as read using specific IDs
*
* @abstract
* @param {string} source Wiki name
* @param {string[]} itemIdArray An array of notification item IDs
* @param {boolean} [isRead] Item's new read state; true for marking the item
* as read, false for marking the item as unread
* @return {jQuery.Promise} A promise that resolves when all given notifications
* are marked as read.
*/
mw.echo.api.APIHandler.prototype.markItemsRead = null;
/**
* Update the read status of a notification item in the API
*
* @param {string} itemId Item id
* @param {boolean} [isRead] Item's new read state; true for marking the item
* as read, false for marking the item as unread
* @return {jQuery.Promise} A promise that resolves when the notifications
* are marked as read.
*/
mw.echo.api.APIHandler.prototype.markItemRead = function ( itemId, isRead ) {
return this.markItemsRead( [ itemId ], isRead );
};
/**
* Query the API for unread count of the notifications in this model
*
* @param {string} type Notification type 'message', 'alert' or 'all'.
* @return {jQuery.Promise} jQuery promise that's resolved when the unread count is fetched
* and the badge label is updated.
*/
mw.echo.api.APIHandler.prototype.fetchUnreadCount = null;
/**
* Check whether the model has an API error state flagged
*
* @param {string} type Notification type, 'alert', 'message' or 'all'
* @param {string|string[]} sources Source names
* @return {boolean} The model is in API error state
*/
mw.echo.api.APIHandler.prototype.isFetchingErrorState = function ( type, sources ) {
var fetchingSource = 'local';
if ( !this.isSourceLocal( sources ) ) {
fetchingSource = 'foreign';
}
return !!( this.apiErrorState[ type ] && this.apiErrorState[ type ][ fetchingSource ] );
};
/**
* Return the fetch notifications promise
*
* @param {string} type Notification type, 'alert', 'message' or 'all'
* @param {string|string[]} [sources] A name of a source or an array of sources to query
* @param {Object} [overrideParams] An object defining parameters to override in the API
* fetching call.
* @return {jQuery.Promise} Promise that is resolved when notifications are
* fetched from the API.
*/
mw.echo.api.APIHandler.prototype.getFetchNotificationPromise = function ( type, sources, overrideParams ) {
var fetchingSource = 'local';
if ( !this.isSourceLocal( sources ) ) {
fetchingSource = 'foreign';
}
if ( overrideParams || !this.fetchNotificationsPromise[ type ] || !this.fetchNotificationsPromise[ type ][ fetchingSource ] ) {
this.createNewFetchNotificationPromise( type, sources, overrideParams );
}
return this.fetchNotificationsPromise[ type ][ fetchingSource ];
};
/**
* Get the extra parameters for fetching notifications for a given
* notification type.
*
* @param {string} type Notification type
* @return {Object} Extra API parameters for fetch notifications
*/
mw.echo.api.APIHandler.prototype.getTypeParams = function ( type ) {
return this.typeParams[ type ];
};
}() );