var c = mw.config.get.bind( mw.config );
// Support both 1 or "1" (T54542)
var isDebugMode = Number( mw.user.options.get( 'eventlogging-display-web' ) ) === 1 ||
Number( mw.user.options.get( 'eventlogging-display-console' ) ) === 1;
// Module-local cache for the result of MediaWikiMetricsClientIntegration::getContextAttributes().
// Since the result of ::getContextAttributes() does not vary by instance, it is safe to cache the
// result at this level.
var contextAttributes = null;
/**
* @classdesc Adapts the MediaWiki execution environment for the JavaScript Metrics Platform Client.
*
* See [Metrics Platform](https://wikitech.wikimedia.org/wiki/Metrics_Platform) on Wikitech.
*
* @class MediaWikiMetricsClientIntegration
* @param {Object} eventLog
* @param {Object} eventLogConfig
*/
function MediaWikiMetricsClientIntegration( eventLog, eventLogConfig ) {
this.eventLog = eventLog;
this.eventLogConfig = eventLogConfig;
}
/**
* Enqueues the event to be submitted to the event ingestion service.
*
* @param {Object} eventData
*/
MediaWikiMetricsClientIntegration.prototype.enqueueEvent = function ( eventData ) {
var serviceUri = this.eventLogConfig.serviceUri;
if ( serviceUri ) {
this.eventLog.enqueue( function () {
try {
navigator.sendBeacon(
serviceUri,
JSON.stringify( eventData )
);
} catch ( e ) {
// Ignore. See T86680, T273374, and T308311.
}
} );
}
};
/**
* Called when an event is enqueued to be submitted to the event ingestion service.
*
* @param {string} streamName
* @param {Object} eventData
*/
MediaWikiMetricsClientIntegration.prototype.onSubmit = function ( streamName, eventData ) {
if ( isDebugMode ) {
mw.track(
'eventlogging.eventSubmitDebug',
{ streamName: streamName, eventData: eventData }
);
}
};
/**
* Gets the hostname of the current document.
*
* @param {string} string
*/
MediaWikiMetricsClientIntegration.prototype.logWarning = function ( string ) {
mw.log.warn( string );
};
/**
* Logs the warning to whatever logging backend that the execution environment, e.g. the
* console.
*
* @return {string}
*/
MediaWikiMetricsClientIntegration.prototype.getHostname = function () {
return String( c( 'wgServerName' ) );
};
/**
* Clones the object deeply.
*
* @param {Object} obj
* @return {Object}
*/
MediaWikiMetricsClientIntegration.prototype.clone = function ( obj ) {
return $.extend( true, {}, obj );
};
/**
* Gets the values for those context attributes that are available in the execution
* environment.
*
* @return {Object}
*/
MediaWikiMetricsClientIntegration.prototype.getContextAttributes = function () {
if ( contextAttributes ) {
return contextAttributes;
}
// TODO: Replace this with whatever config variable is decided on in
// https://phabricator.wikimedia.org/T299772.
//
// This used to be determined by checking whether <body> had the "mw-mf" class. However, this
// was determined to be a non-trivial read from the DOM and one that could cause a forced style
// recalculation in certain situations.
//
// See https://gerrit.wikimedia.org/r/c/mediawiki/extensions/WikimediaEvents/+/799353/1#message-21b63aebf69dc330933ef27deb11279b226656b8
// for a detailed explanation.
var isMobileFrontendActive = c( 'wgMFMode' ) !== null;
var version = String( c( 'wgVersion' ) );
var userIsLoggedIn = !mw.user.isAnon();
var userGroups = c( 'wgUserGroups' );
/* eslint-disable camelcase */
var result = {
agent: {
client_platform: 'mediawiki_js',
client_platform_family: isMobileFrontendActive ? 'mobile_browser' : 'desktop_browser'
},
page: {
id: c( 'wgArticleId' ),
title: c( 'wgTitle' ),
namespace_id: c( 'wgNamespaceNumber' ),
namespace_name: c( 'wgCanonicalNamespace' ),
revision_id: c( 'wgRevisionId' ),
// The wikidata_id (int) context attribute is deprecated in favor of wikidata_qid
// (string). See T330459 and T332673 for detail.
wikidata_qid: c( 'wgWikibaseItemId' ),
content_language: c( 'wgPageContentLanguage' ),
is_redirect: c( 'wgIsRedirect' ),
user_groups_allowed_to_move: c( 'wgRestrictionMove' ),
user_groups_allowed_to_edit: c( 'wgRestrictionEdit' )
},
mediawiki: {
skin: c( 'skin' ),
version: version,
is_production: version.indexOf( 'wmf' ) !== -1,
is_debug_mode: isDebugMode,
database: c( 'wgDBname' ),
site_content_language: c( 'wgContentLanguage' )
},
performer: {
is_logged_in: userIsLoggedIn,
id: mw.user.getId(),
name: mw.user.getName(),
// NOTE: This method is expected to execute synchronously. mw.user.getGroups returns a
// promise (jQuery.Promise) so get the information from the global config instead.
groups: userGroups,
// NOTE: As above, this method is expected to execute synchronously. We should test
// whether the user has the "bot" right but mw.user.getRights() returns a promise
// (jQuery.Promise). Fortunately, the "bot" group, which grants users the "bot" right,
// is a default MediaWiki user group [0].
//
// [0] https://www.mediawiki.org/wiki/Help:User_rights_and_groups#User_rights_and_groups_on_your_wiki
is_bot: userGroups.indexOf( 'bot' ) !== -1,
is_temp: c( 'wgUserIsTemp' ),
language: c( 'wgUserLanguage' ),
language_variant: c( 'wgUserVariant' ),
can_probably_edit_page: c( 'wgIsProbablyEditable' )
}
};
if ( userIsLoggedIn ) {
result.performer.edit_count = c( 'wgUserEditCount' );
result.performer.edit_count_bucket = c( 'wgUserEditCountBucket' );
result.performer.registration_dt = new Date( c( 'wgUserRegistration' ) ).toISOString();
}
/* eslint-enable camelcase */
var self = this;
Object.defineProperty( result.performer, 'session_id', {
get: function () {
return self.getSessionId();
}
} );
Object.defineProperty( result.performer, 'pageview_id', {
get: function () {
return self.getPageviewId();
}
} );
Object.defineProperty( result.performer, 'active_browsing_session_token', {
get: function () {
return mw.eventLog.id.getSessionId();
}
} );
contextAttributes = result;
return result;
};
// NOTE: The following are required for compatibility with the current impl. but the
// information is also available via ::getContextualAttributes() above.
/**
* Gets a token unique to the current pageview within the execution environment.
*
* @return {string}
*/
MediaWikiMetricsClientIntegration.prototype.getPageviewId = function () {
return mw.user.getPageviewToken();
};
/**
* Gets a token unique to the current session within the execution environment.
*
* @return {string}
*/
MediaWikiMetricsClientIntegration.prototype.getSessionId = function () {
return mw.user.sessionId();
};
module.exports = MediaWikiMetricsClientIntegration;