From dcecf76ff1ae3daf6c72b500e04ec802f082982f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Dziewo=C5=84ski?= Date: Sat, 18 Mar 2023 03:29:27 +0100 Subject: [PATCH] Centralize EditAttemptStep logging code in WikimediaEvents PHP logging code is not moved. * Use the new mw.track() handlers from WikimediaEvents * Ensure that 'integration' and 'editor_interface' are set on init events, since they're not hard-coded in the handler any more * Remove the setting of 'editingStatsId' tracking parameter, now happens in WikimediaEvents (by way of VE ArticleTargetSaver) * Remove code connecting ve.track to mw.track, now happens in VE This must be merged together with WikimediaEvents change Iace4d53a972396ca5b8713000570cc47c9986034 (but we can't use Depends-On, because CI requires code here to be removed first). Bug: T332438 Change-Id: I0ef0a96aafdf89a4ebe32131a85b18c25744bb2c --- extension.json | 9 - includes/Hooks/ResourceLoaderHooks.php | 5 - includes/Notifications/EventDispatcher.php | 4 +- modules/CommentController.js | 15 +- modules/NewTopicController.js | 4 +- modules/controller.js | 3 +- modules/dt.init.js | 4 +- modules/dt.ui.ReplyWidget.js | 27 +- modules/dt.ui.ReplyWidgetVisual.js | 6 - modules/logger.js | 292 --------------------- 10 files changed, 26 insertions(+), 343 deletions(-) delete mode 100644 modules/logger.js diff --git a/extension.json b/extension.json index 6737db2ff..c057d844c 100644 --- a/extension.json +++ b/extension.json @@ -67,7 +67,6 @@ "ReplyLinksController.js", "CommentController.js", "NewTopicController.js", - "logger.js", "modifier.js", "utils.js", "Parser.js", @@ -565,14 +564,6 @@ "DiscussionToolsConflictingGadgetName": { "value": "convenientDiscussions", "description": "Internal name of the Convenient Discussions gadget, which conflicts with the reply tool. Reply tool will be unavailable when the user has this gadget enabled." - }, - "DTSchemaEditAttemptStepSamplingRate": { - "value": 0, - "description": "Rate at which to sample sessions for instrumentation; overrides WikimediaEvents rate if set to any number other than 0" - }, - "DTSchemaEditAttemptStepOversample": { - "value": false, - "description": "Oversample EditAttemptStep logging; distinct from rate tuning, as it'll flag the events as being oversampled" } }, "ConfigRegistry": { diff --git a/includes/Hooks/ResourceLoaderHooks.php b/includes/Hooks/ResourceLoaderHooks.php index ee6656590..9ec0c6475 100644 --- a/includes/Hooks/ResourceLoaderHooks.php +++ b/includes/Hooks/ResourceLoaderHooks.php @@ -34,11 +34,6 @@ class ResourceLoaderHooks implements * @param Config $config */ public function onResourceLoaderGetConfigVars( array &$vars, $skin, Config $config ): void { - $vars['wgDTSchemaEditAttemptStepSamplingRate'] = - $this->config->get( 'DTSchemaEditAttemptStepSamplingRate' ); - $vars['wgDTSchemaEditAttemptStepOversample'] = - $this->config->get( 'DTSchemaEditAttemptStepOversample' ); - $abtest = $this->config->get( 'DiscussionToolsABTest' ); if ( $abtest ) { $vars['wgDiscussionToolsABTest'] = $abtest; diff --git a/includes/Notifications/EventDispatcher.php b/includes/Notifications/EventDispatcher.php index 6e2b74f46..acceb280e 100644 --- a/includes/Notifications/EventDispatcher.php +++ b/includes/Notifications/EventDispatcher.php @@ -423,9 +423,11 @@ class EventDispatcher { if ( !$extensionRegistry->isLoaded( 'EventLogging' ) ) { return false; } + if ( !$extensionRegistry->isLoaded( 'WikimediaEvents' ) ) { + return false; + } $inSample = static::inEventSample( $editingStatsId ); $shouldOversample = ( $isDiscussionTools && $wgDTSchemaEditAttemptStepOversample ) || ( - $extensionRegistry->isLoaded( 'WikimediaEvents' ) && // @phan-suppress-next-line PhanUndeclaredClassMethod \WikimediaEvents\WikimediaEventsHooks::shouldSchemaEditAttemptStepOversample( $context ) ); diff --git a/modules/CommentController.js b/modules/CommentController.js index 616a99573..dc0d73e88 100644 --- a/modules/CommentController.js +++ b/modules/CommentController.js @@ -1,7 +1,6 @@ var controller = require( './controller.js' ), modifier = require( './modifier.js' ), - logger = require( './logger.js' ), dtConf = require( './config.json' ), CommentItem = require( './CommentItem.js' ), scrollPadding = { @@ -141,10 +140,11 @@ CommentController.prototype.setup = function ( mode, hideErrors, suppressNotific ( defaultVisual ? 'visual' : 'source' ); } - logger( { + mw.track( 'editAttemptStep', { action: 'init', type: this.constructor.static.initType || 'page', mechanism: 'click', + integration: 'discussiontools', // eslint-disable-next-line camelcase editor_interface: mode === 'visual' ? 'visualeditor' : ( enable2017Wikitext ? 'wikitext-2017' : 'wikitext' ) @@ -164,7 +164,7 @@ CommentController.prototype.setup = function ( mode, hideErrors, suppressNotific mw.track( 'dt.commentSetupError', code ); } - logger( { + mw.track( 'editAttemptStep', { action: 'abort', type: 'preinit' } ); @@ -229,8 +229,8 @@ CommentController.prototype.setup = function ( mode, hideErrors, suppressNotific commentController.showAndFocus(); - logger( { action: 'ready' } ); - logger( { action: 'loaded' } ); + mw.track( 'editAttemptStep', { action: 'ready' } ); + mw.track( 'editAttemptStep', { action: 'loaded' } ); } ); }; @@ -437,8 +437,7 @@ CommentController.prototype.getApiQuery = function ( pageName, checkboxes ) { // HACK: Always display reply links afterwards, ignoring preferences etc., in case this was // a page view with reply links forced with ?dtenable=1 or otherwise dtenable: '1', - dttags: tags.join( ',' ), - editingStatsId: logger.getSessionId() + dttags: tags.join( ',' ) }; if ( replyWidget.getMode() === 'source' ) { @@ -725,7 +724,7 @@ CommentController.prototype.switchToVisual = function () { size: 'medium' } ); - mw.track( 'dt.schemaVisualEditorFeatureUse', { + mw.track( 'visualEditorFeatureUse', { feature: 'editor-switch', action: 'dialog-prevent-show' } ); diff --git a/modules/NewTopicController.js b/modules/NewTopicController.js index ad127d8c6..0baae745b 100644 --- a/modules/NewTopicController.js +++ b/modules/NewTopicController.js @@ -1,5 +1,4 @@ var - logger = require( './logger.js' ), controller = require( './controller.js' ), CommentController = require( './CommentController.js' ), HeadingItem = require( './HeadingItem.js' ); @@ -332,8 +331,7 @@ NewTopicController.prototype.getApiQuery = function ( pageName, checkboxes ) { data = $.extend( {}, data, { paction: 'addtopic', sectiontitle: this.sectionTitle.getValue(), - dttags: tags.join( ',' ), - editingStatsId: logger.getSessionId() + dttags: tags.join( ',' ) } ); // Allow MediaWiki to generate the summary if it wasn't modified by the user. This avoids diff --git a/modules/controller.js b/modules/controller.js index 956e15f85..6bcf936ac 100644 --- a/modules/controller.js +++ b/modules/controller.js @@ -12,7 +12,6 @@ var CommentDetails = require( './CommentDetails.js' ), HeadingItem = require( './HeadingItem.js' ), ReplyLinksController = require( './ReplyLinksController.js' ), - logger = require( './logger.js' ), utils = require( './utils.js' ), highlighter = require( './highlighter.js' ), topicSubscriptions = require( './topicsubscriptions.js' ), @@ -612,7 +611,7 @@ function refreshPageContents( oldId ) { */ function update( data, threadItem, pageName, replyWidget ) { function logSaveSuccess() { - logger( { + mw.track( 'editAttemptStep', { action: 'saveSuccess', timing: mw.now() - replyWidget.saveInitiated, // eslint-disable-next-line camelcase diff --git a/modules/dt.init.js b/modules/dt.init.js index 7f857d605..5ca460d08 100644 --- a/modules/dt.init.js +++ b/modules/dt.init.js @@ -16,7 +16,6 @@ if ( mw.user.isAnon() && mw.config.get( 'wgDiscussionToolsABTest' ) && mw.config var token = mw.cookie.get( 'DTABid', '', mw.user.generateRandomSessionId() ); mw.cookie.set( 'DTAB', mw.config.get( 'wgDiscussionToolsABTestBucket' ), { path: '/', expires: 90 * 86400, prefix: '' } ); mw.cookie.set( 'DTABid', token, { path: '/', expires: 90 * 86400, prefix: '' } ); - mw.config.set( 'wgDiscussionToolsAnonymousUserId', token ); } if ( url.searchParams.get( 'dtrepliedto' ) ) { @@ -89,6 +88,5 @@ module.exports = { HeadingItem: require( './HeadingItem.js' ), CommentItem: require( './CommentItem.js' ), utils: require( './utils.js' ), - config: require( './config.json' ), - logger: require( './logger.js' ) + config: require( './config.json' ) }; diff --git a/modules/dt.ui.ReplyWidget.js b/modules/dt.ui.ReplyWidget.js index 9eed3a199..4609a2cd1 100644 --- a/modules/dt.ui.ReplyWidget.js +++ b/modules/dt.ui.ReplyWidget.js @@ -1,6 +1,5 @@ var controller = require( 'ext.discussionTools.init' ).controller, utils = require( 'ext.discussionTools.init' ).utils, - logger = require( 'ext.discussionTools.init' ).logger, ModeTabSelectWidget = require( './ModeTabSelectWidget.js' ), ModeTabOptionWidget = require( './ModeTabOptionWidget.js' ), licenseMessages = require( './licenseMessages.json' ), @@ -291,7 +290,7 @@ function ReplyWidget( commentController, commentDetails, config ) { this.checkboxesPromise = controller.getCheckboxesPromise( this.pageName, this.oldId ); this.checkboxesPromise.then( function ( checkboxes ) { function trackCheckbox( n ) { - mw.track( 'dt.schemaVisualEditorFeatureUse', { + mw.track( 'visualEditorFeatureUse', { feature: 'dtReply', action: 'checkbox-' + n } ); @@ -452,7 +451,7 @@ ReplyWidget.prototype.saveEditMode = function ( mode ) { ReplyWidget.prototype.onAdvancedToggleClick = function () { var showAdvanced = !this.showAdvanced; - mw.track( 'dt.schemaVisualEditorFeatureUse', { + mw.track( 'visualEditorFeatureUse', { feature: 'dtReply', action: 'advanced-' + ( showAdvanced ? 'show' : 'hide' ) } ); @@ -546,7 +545,7 @@ ReplyWidget.prototype.switch = function ( mode ) { // reload the editor. return promise.then( function () { // Switch succeeded - mw.track( 'dt.schemaVisualEditorFeatureUse', { + mw.track( 'visualEditorFeatureUse', { feature: 'editor-switch', action: ( mode === 'visual' ? @@ -661,7 +660,7 @@ ReplyWidget.prototype.tryTeardown = function () { if ( !( data && data.action === 'discard' ) ) { return $.Deferred().reject().promise(); } - logger( { + mw.track( 'editAttemptStep', { action: 'abort', mechanism: 'cancel', type: 'abandon' @@ -669,7 +668,7 @@ ReplyWidget.prototype.tryTeardown = function () { } ); } else { promise = $.Deferred().resolve().promise(); - logger( { + mw.track( 'editAttemptStep', { action: 'abort', mechanism: 'cancel', type: 'nochange' @@ -839,7 +838,7 @@ ReplyWidget.prototype.updateButtons = function () { * Currently only the first change in the body, used for logging. */ ReplyWidget.prototype.onFirstChange = function () { - logger( { action: 'firstChange' } ); + mw.track( 'editAttemptStep', { action: 'firstChange' } ); }; /** @@ -883,7 +882,7 @@ ReplyWidget.prototype.onBeforeUnload = function ( e ) { * @param {jQuery.Event} e Event */ ReplyWidget.prototype.onUnload = function () { - logger( { + mw.track( 'editAttemptStep', { action: 'abort', type: this.isEmpty() ? 'nochange' : 'abandon', mechanism: 'navigate' @@ -951,7 +950,7 @@ ReplyWidget.prototype.updateNewCommentsWarning = function ( comments ) { setTimeout( function () { widget.newCommentsWarning.$element.addClass( 'ext-discussiontools-ui-replyWidget-newComments-open' ); } ); - mw.track( 'dt.schemaVisualEditorFeatureUse', { + mw.track( 'visualEditorFeatureUse', { feature: 'notificationNewComments', action: 'show' } ); @@ -963,7 +962,7 @@ ReplyWidget.prototype.updateNewCommentsWarning = function ( comments ) { */ ReplyWidget.prototype.onNewCommentsShowClick = function () { this.emit( 'reloadPage' ); - mw.track( 'dt.schemaVisualEditorFeatureUse', { + mw.track( 'visualEditorFeatureUse', { feature: 'notificationNewComments', action: 'page-update' } ); @@ -977,7 +976,7 @@ ReplyWidget.prototype.onNewCommentsCloseClick = function () { // Hide the warning for the rest of the lifetime of the widget this.hideNewCommentsWarning = true; this.focus(); - mw.track( 'dt.schemaVisualEditorFeatureUse', { + mw.track( 'visualEditorFeatureUse', { feature: 'notificationNewComments', action: 'close' } ); @@ -1018,12 +1017,12 @@ ReplyWidget.prototype.onReplyClick = function () { this.saveInitiated = mw.now(); this.setPending( true ); - logger( { action: 'saveIntent' } ); + mw.track( 'editAttemptStep', { action: 'saveIntent' } ); // TODO: When editing a transcluded page, VE API returning the page HTML is a waste, since we won't use it var pageName = this.pageName; - logger( { action: 'saveAttempt' } ); + mw.track( 'editAttemptStep', { action: 'saveAttempt' } ); widget.commentController.save( pageName ).fail( function ( code, data ) { // Compare to ve.init.mw.ArticleTargetEvents.js in VisualEditor. var typeMap = { @@ -1089,7 +1088,7 @@ ReplyWidget.prototype.onReplyClick = function () { } } - logger( { + mw.track( 'editAttemptStep', { action: 'saveFailure', timing: mw.now() - widget.saveInitiated, message: code, diff --git a/modules/dt.ui.ReplyWidgetVisual.js b/modules/dt.ui.ReplyWidgetVisual.js index ac57856f0..41f777538 100644 --- a/modules/dt.ui.ReplyWidgetVisual.js +++ b/modules/dt.ui.ReplyWidgetVisual.js @@ -172,10 +172,4 @@ ReplyWidgetVisual.prototype.focus = function () { return this; }; -ve.trackSubscribe( 'activity.', function ( topic, data ) { - mw.track( 'dt.schemaVisualEditorFeatureUse', ve.extendObject( data, { - feature: topic.split( '.' )[ 1 ] - } ) ); -} ); - module.exports = ReplyWidgetVisual; diff --git a/modules/logger.js b/modules/logger.js deleted file mode 100644 index e6c637e5e..000000000 --- a/modules/logger.js +++ /dev/null @@ -1,292 +0,0 @@ -'use strict'; - -var trackdebug = !!mw.util.getParamValue( 'trackdebug' ), - featuresEnabled = mw.config.get( 'wgDiscussionToolsFeaturesEnabled' ) || {}, - enable2017Wikitext = featuresEnabled.sourcemodetoolbar, - session = { - // Create an initial session ID in case any VisualEditorFeatureUse events - // trigger before our first init event - // eslint-disable-next-line camelcase - editing_session_id: mw.user.generateRandomSessionId() - }; - -/** - * Logs an event to http://meta.wikimedia.org/wiki/Schema:EditAttemptStep - * - * @instance - * @param {Object} data - */ -module.exports = function ( data ) { - mw.track( 'dt.schemaEditAttemptStep', data ); -}; -module.exports.getSessionId = function () { - return session.editing_session_id; -}; - -// Ensure 'ext.eventLogging' first, it provides mw.eventLog.randomTokenMatch. -// (No explicit dependency is set because we want this to just quietly not-happen -// if EventLogging isn't installed.) -mw.loader.using( 'ext.eventLogging' ).done( function () { - var // Schema class is provided by ext.eventLogging - Schema = mw.eventLog.Schema, - user = mw.user, - easSampleRate = mw.config.get( 'wgDTSchemaEditAttemptStepSamplingRate' ) || - mw.config.get( 'wgWMESchemaEditAttemptStepSamplingRate' ), - vefuSampleRate = mw.config.get( 'wgWMESchemaVisualEditorFeatureUseSamplingRate' ) || easSampleRate, - actionPrefixMap = { - firstChange: 'first_change', - saveIntent: 'save_intent', - saveAttempt: 'save_attempt', - saveSuccess: 'save_success', - saveFailure: 'save_failure' - }, - timing = {}, - firstInitDone = false, - /** - * Edit schema - * https://meta.wikimedia.org/wiki/Schema:EditAttemptStep - */ - /* eslint-disable camelcase */ - schemaEditAttemptStep = new Schema( - 'EditAttemptStep', - easSampleRate, - // defaults: - { - page_id: mw.config.get( 'wgArticleId' ), - revision_id: mw.config.get( 'wgRevisionId' ), - page_title: mw.config.get( 'wgPageName' ), - page_ns: mw.config.get( 'wgNamespaceNumber' ), - user_id: user.getId(), - user_class: user.isAnon() ? 'IP' : undefined, - user_editcount: mw.config.get( 'wgUserEditCount', 0 ), - mw_version: mw.config.get( 'wgVersion' ), - // T249944 may someday change this to not hang from MobileFrontend - platform: mw.config.get( 'wgMFMode' ) !== null ? 'phone' : 'desktop', - integration: 'discussiontools', - page_token: user.getPageviewToken(), - session_token: user.sessionId(), - version: 1 - } - ), - schemaVisualEditorFeatureUse = new Schema( - 'VisualEditorFeatureUse', - vefuSampleRate, - // defaults: - { - user_id: user.getId(), - user_editcount: mw.config.get( 'wgUserEditCount', 0 ), - // T249944 may someday change this to not hang from MobileFrontend - platform: mw.config.get( 'wgMFMode' ) !== null ? 'phone' : 'desktop', - integration: 'discussiontools' - } - ); - /* eslint-enable camelcase */ - - function log() { - // mw.log is a no-op unless resource loader is in debug mode, so - // this allows trackdebug to work independently - // eslint-disable-next-line no-console - console.log.apply( console, arguments ); - } - - function computeDuration( action, event, timeStamp ) { - // This is duplicated from the VisualEditor extension - // (ve.init.mw.trackSubscriber.js). Changes to this should be kept in - // sync with that file, so the data remains consistent. - if ( event.timing !== undefined ) { - return event.timing; - } - - switch ( action ) { - case 'ready': - return timeStamp - timing.init; - case 'loaded': - return timeStamp - timing.init; - case 'firstChange': - return timeStamp - timing.ready; - case 'saveIntent': - return timeStamp - timing.ready; - case 'saveAttempt': - return timeStamp - timing.saveIntent; - case 'saveSuccess': - case 'saveFailure': - // HERE BE DRAGONS: the caller must compute these themselves - // for sensible results. Deliberately sabotage any attempts to - // use the default by returning -1 - mw.log.warn( 'dt.schemaEditAttemptStep: Do not rely on default timing value for saveSuccess/saveFailure' ); - return -1; - case 'abort': - switch ( event.abort_type ) { - case 'preinit': - return timeStamp - timing.init; - case 'nochange': - case 'switchwith': - case 'switchwithout': - case 'switchnochange': - case 'abandon': - return timeStamp - timing.ready; - case 'abandonMidsave': - return timeStamp - timing.saveAttempt; - } - mw.log.warn( 'dt.schemaEditAttemptStep: Unrecognized abort type', event.type ); - return -1; - } - mw.log.warn( 'dt.schemaEditAttemptStep: Unrecognized action', action ); - return -1; - } - - mw.trackSubscribe( 'dt.schemaEditAttemptStep', function ( topic, data ) { - var actionPrefix = actionPrefixMap[ data.action ] || data.action, - timeStamp = mw.now(), - duration = 0; - - // Update the rolling session properties - if ( data.action === 'init' ) { - if ( firstInitDone ) { - // eslint-disable-next-line camelcase - session.editing_session_id = mw.user.generateRandomSessionId(); - } - firstInitDone = true; - } - // eslint-disable-next-line camelcase - session.editor_interface = data.editor_interface || session.editor_interface; - - // Schema's kind of a mess of special properties - if ( data.action === 'init' || data.action === 'abort' || data.action === 'saveFailure' ) { - data[ actionPrefix + '_type' ] = data.type; - } - if ( data.action === 'init' || data.action === 'abort' ) { - data[ actionPrefix + '_mechanism' ] = data.mechanism; - } - if ( data.action !== 'init' ) { - // Schema actually does have an init_timing field, but we don't want to - // store it because it's not meaningful. - duration = Math.round( computeDuration( data.action, data, timeStamp ) ); - data[ actionPrefix + '_timing' ] = duration; - } - if ( data.action === 'saveFailure' ) { - data[ actionPrefix + '_message' ] = data.message; - } - - // Remove renamed properties - delete data.type; - delete data.mechanism; - delete data.timing; - delete data.message; - // eslint-disable-next-line camelcase - data.is_oversample = - !mw.eventLog.inSample( 1 / easSampleRate ); - - if ( data.action === 'abort' && data.abort_type !== 'switchnochange' ) { - timing = {}; - } else { - timing[ data.action ] = timeStamp; - } - - // Switching between visual and source produces a chain of - // abort/ready/loaded events and no init event, so suppress them for - // consistency with desktop VE's logging. - if ( data.abort_type === 'switchnochange' ) { - // The initial abort, flagged as a switch - return; - } - if ( timing.abort ) { - // An abort was previously logged - if ( data.action === 'ready' ) { - // Just discard the ready - return; - } - if ( data.action === 'loaded' ) { - // Switch has finished; remove the abort timing so we stop discarding events. - delete timing.abort; - return; - } - } - - if ( mw.config.get( 'wgDiscussionToolsABTestBucket' ) ) { - data.bucket = mw.config.get( 'wgDiscussionToolsABTestBucket' ); - if ( mw.user.isAnon() && mw.config.get( 'wgDiscussionToolsAnonymousUserId' ) ) { - // eslint-disable-next-line camelcase - data.anonymous_user_token = mw.config.get( 'wgDiscussionToolsAnonymousUserId' ); - } - } - - $.extend( data, session ); - - if ( trackdebug ) { - log( topic + '.' + data.action, duration + 'ms', data, schemaEditAttemptStep.defaults ); - } else { - schemaEditAttemptStep.log( - data, - ( - mw.config.get( 'wgDTSchemaEditAttemptStepOversample' ) || - mw.config.get( 'wgWMESchemaEditAttemptStepOversample' ) - ) ? 1 : easSampleRate - ); - - // T309013: Also log via the Metrics Platform: - var eventName = 'eas.dt.' + actionPrefix; - var customData = $.extend( - { - integration: 'discussiontools' - }, - data - ); - - delete customData.action; - - // Sampling rate (and therefore whether a stream should oversample) is captured in the - // stream config ($wgEventStreams). - delete customData.is_oversample; - - mw.eventLog.dispatch( eventName, customData ); - } - } ); - - mw.trackSubscribe( 'dt.schemaVisualEditorFeatureUse', function ( topic, data ) { - // eslint-disable-next-line camelcase - session.editor_interface = data.editor_interface || session.editor_interface; - - var event = { - feature: data.feature, - action: data.action, - editingSessionId: session.editing_session_id, - // eslint-disable-next-line camelcase - editor_interface: session.editor_interface - }; - - if ( mw.config.get( 'wgDiscussionToolsABTestBucket' ) ) { - event.bucket = mw.config.get( 'wgDiscussionToolsABTestBucket' ); - } - - if ( trackdebug ) { - log( topic, event, schemaVisualEditorFeatureUse.defaults ); - } else { - schemaVisualEditorFeatureUse.log( event, ( - mw.config.get( 'wgDTSchemaEditAttemptStepOversample' ) || - mw.config.get( 'wgWMESchemaEditAttemptStepOversample' ) - ) ? 1 : vefuSampleRate ); - - // T309602: Also log via the Metrics Platform: - var eventName = 'vefu.' + data.action; - - /* eslint-disable camelcase */ - var customData = { - feature: data.feature, - editing_session_id: session.editing_session_id, - editor_interface: session.editor_interface, - integration: 'discussiontools' - }; - /* eslint-enable camelcase */ - - mw.eventLog.dispatch( eventName, customData ); - } - - if ( data.feature === 'editor-switch' && data.action.indexOf( 'dialog-' ) === -1 ) { - // eslint-disable-next-line camelcase - session.editor_interface = session.editor_interface === 'visualeditor' ? - ( enable2017Wikitext ? 'wikitext-2017' : 'wikitext' ) : - 'visualeditor'; - } - } ); -} );