diff --git a/Resources.php b/Resources.php index 6ae9c6b5..c80b22ab 100644 --- a/Resources.php +++ b/Resources.php @@ -66,6 +66,11 @@ $wgResourceModules['ext.uls.init'] = array( 'position' => 'top', ) + $resourcePaths; +$wgResourceModules['ext.uls.eventlogger'] = array( + 'scripts' => 'resources/js/ext.uls.eventlogger.js', + 'dependencies' => 'schema.UniversalLanguageSelector', +) + $resourcePaths; + $wgResourceModules['ext.uls.i18n'] = array( 'scripts' => 'resources/js/ext.uls.i18n.js', 'dependencies' => 'jquery.i18n', diff --git a/UniversalLanguageSelector.hooks.php b/UniversalLanguageSelector.hooks.php index 0450ca15..3fc37be1 100644 --- a/UniversalLanguageSelector.hooks.php +++ b/UniversalLanguageSelector.hooks.php @@ -48,9 +48,13 @@ class UniversalLanguageSelectorHooks { // Load the style for users without JS, to hide the useless links $out->addModuleStyles( 'ext.uls.nojs' ); - // If EventLogging integration is enabled, load the schema module. + // If EventLogging integration is enabled, load the schema module + // and the event logging functions module if ( $wgULSEventLogging ) { - $out->addModules( 'schema.UniversalLanguageSelector' ); + $out->addModules( array( + 'schema.UniversalLanguageSelector', + 'ext.uls.eventlogger', + ) ); } // If the extension is enabled, basic features (API, language data) available. diff --git a/resources/js/ext.uls.displaysettings.js b/resources/js/ext.uls.displaysettings.js index 0e6a51eb..8cffd853 100644 --- a/resources/js/ext.uls.displaysettings.js +++ b/resources/js/ext.uls.displaysettings.js @@ -159,18 +159,17 @@ new mw.Api().parse( $.i18n( 'ext-uls-display-settings-anon-log-in-cta' ) ) .done( function ( parsedCta ) { $loginCta.html( parsedCta ); - // Because browsers navigate away when clicking a link, - // we are are overriding the normal click behavior to - // allow the event be logged first - currently there is no - // local queue for events. The timeout is there to make sure - // the user gets to the new page even if event logging is slow - // or fails. $loginCta.find( 'a' ).click( function ( event ) { event.preventDefault(); - mw.uls.logEvent( { action: 'login-click' }, 500 ) - .always( function () { - window.location.href = event.target.href; - } ); + // Because browsers navigate away when clicking a link, + // we are are overriding the normal click behavior to + // allow the event be logged first - currently there is no + // local queue for events. The timeout is there to make sure + // the user gets to the new page even if event logging is slow + // or fails. + mw.hook( 'mw.uls.login.click' ).fire( function () { + window.location.href = event.target.href; + } ); } ); } ); @@ -323,10 +322,7 @@ $moreLanguagesButton.on( 'click', function () { displaySettings.$parent.hide(); - mw.uls.logEvent( { - action: 'more-languages-access', - context: 'interface' - } ); + mw.hook( 'mw.uls.interface.morelanguages' ).fire(); } ); }, diff --git a/resources/js/ext.uls.eventlogger.js b/resources/js/ext.uls.eventlogger.js new file mode 100644 index 00000000..3631e05c --- /dev/null +++ b/resources/js/ext.uls.eventlogger.js @@ -0,0 +1,181 @@ +/** + * ULS Event logger + * + * Copyright (C) 2012-2013 Alolita Sharma, Amir Aharoni, Arun Ganesh, Brandon Harris, + * Niklas Laxström, Pau Giner, Santhosh Thottingal, Siebrand Mazeland and other + * contributors. See CREDITS for a list. + * + * UniversalLanguageSelector is dual licensed GPLv2 or later and MIT. You don't + * have to do anything special to choose one license or the other and you don't + * have to notify anyone which license you are using. You are free to use + * UniversalLanguageSelector in commercial projects as long as the copyright + * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details. + * + * @file + * @ingroup Extensions + * @licence GNU General Public Licence 2.0 or later + * @licence MIT License + */ + +( function ( $, mw ) { + 'use strict'; + + /** + * ULS Event logger + * + * @since 2013.08 + * @see https://meta.wikimedia.org/wiki/Schema:UniversalLanguageSelector + */ + function ULSEventLogger() { + this.logEventQueue = $.Callbacks( 'memory once' ); + this.init(); + this.listen(); + } + + ULSEventLogger.prototype = { + init: function () { + var eventLogger = this; + + // Set event defaults and make the + mw.eventLog.setDefaults( 'UniversalLanguageSelector', { + version: 1, + token: mw.user.id(), + contentLanguage: mw.config.get( 'wgContentLanguage' ), + interfaceLanguage: mw.config.get( 'wgUserLanguage' ) + } ); + + eventLogger.logEventQueue.fire(); + }, + + /** + * Local wrapper for 'mw.eventLog.logEvent' which handles default params + * and ensures the correct schema is loaded. + * + * @param {Object} event Event action and optional fields + * @param {int} [timeout] Fail the request if it is not completed within + * the specified timeout. Can be use for example to log actions that + * cause the browser to navigate to other pages. + * @return {jQuery.Promise} jQuery Promise object for the logging call + */ + log: function ( event, timeout ) { + // We need to create our own deferred for two reasons: + // - logEvent might not be executed immediately + // - we cannot reject a promise returned by it + // So we proxy the original promises status updates + // and register our timeout if requested (for links). + var deferred = $.Deferred(); + + this.logEventQueue.add( function () { + mw.eventLog.logEvent( 'UniversalLanguageSelector', event ) + .done( deferred.resolve ) + .fail( deferred.reject ); + } ); + + if ( timeout !== undefined ) { + window.setTimeout( deferred.reject, timeout ); + } + + return deferred.promise(); + }, + + /** + * Listen for event logging + */ + listen: function () { + // Register handlers for event logging triggers + mw.hook( 'mw.uls.settings.open' ).add( $.proxy( this.ulsSettingsOpen, this ) ); + mw.hook( 'mw.uls.language.revert' ).add( $.proxy( this.ulsLanguageRevert, this ) ); + mw.hook( 'mw.uls.ime.enable' ).add( $.proxy( this.enableIME, this ) ); + mw.hook( 'mw.uls.ime.disable' ).add( $.proxy( this.disableIME, this ) ); + mw.hook( 'mw.uls.login.click' ).add( $.proxy( this.loginClick, this ) ); + mw.hook( 'mw.uls.ime.morelanguages' ).add( $.proxy( this.imeMoreLanguages, this ) ); + mw.hook( 'mw.uls.interface.morelanguages' ).add( $.proxy( this.interfaceMoreLanguages, this ) ); + mw.hook( 'mw.uls.interface.language.change' ).add( $.proxy( this.interfaceLanguageChange, this ) ); + }, + + /** + * Log language settings open + * @param {Array} args + */ + ulsSettingsOpen: function ( args ) { + this.log( { + action: 'settings-open', + context: args + } ); + }, + + /** + * Log language revert + * + * @param {Function} callback + */ + ulsLanguageRevert: function ( callback ) { + this.log( { action: 'ui-lang-revert' }, 500 ).always( callback ); + }, + + /** + * Log IME disabling + */ + disableIME: function () { + this.log( { action: 'ime-disable' } ); + }, + + /** + * Log IME enabling + */ + enableIME: function () { + this.log( { action: 'ime-enable' } ); + }, + + /** + * Log login link click in display settings. Since this will navigate + * away from the current page, provide a callback option. + * + * If page is navigating away, event logging will fail because it is not + * yet completed. With the callback, the navigation can be executed in + * callback. The waiting for callback is not indefinite, but with a timeout. + * + * @param {Function} callback + */ + loginClick: function ( callback ) { + this.log( { action: 'login-click' }, 500 ).always( callback ); + }, + + /** + * More languages item in IME menu is clicked + */ + imeMoreLanguages: function () { + this.log( { + action: 'more-languages-access', + context: 'ime' + } ); + }, + + /** + * Log interface language change + * + * @param {string} language language code + * @param {Function} callback + */ + interfaceLanguageChange: function ( language, callback ) { + this.log( { + action: 'language-change', + context: 'interface', + interfaceLanguage: language + }, 500 ).always( callback ); + }, + + /** + * More languages in display settings is clicked + */ + interfaceMoreLanguages: function () { + this.log( { + action: 'more-languages-access', + context: 'interface' + } ); + } + }; + + mw.uls = mw.uls || {}; + mw.uls.eventlogger= new ULSEventLogger(); +}( jQuery, mediaWiki ) ); diff --git a/resources/js/ext.uls.ime.js b/resources/js/ext.uls.ime.js index c1757659..296374b9 100644 --- a/resources/js/ext.uls.ime.js +++ b/resources/js/ext.uls.ime.js @@ -81,13 +81,13 @@ disable: function () { this.registry.isDirty = true; this.registry.enable = false; - mw.uls.logEvent( { action: 'ime-disable' } ); + mw.hook( 'mw.uls.ime.disable' ).fire(); }, enable: function () { this.registry.isDirty = true; this.registry.enable = true; - mw.uls.logEvent( { action: 'ime-enable' } ); + mw.hook( 'mw.uls.ime.enable' ).fire(); }, isEnabled: function () { diff --git a/resources/js/ext.uls.init.js b/resources/js/ext.uls.init.js index 96f6963d..6bfe299b 100644 --- a/resources/js/ext.uls.init.js +++ b/resources/js/ext.uls.init.js @@ -33,8 +33,7 @@ var jsonLoader = null, initialized = false, - currentLang = mw.config.get( 'wgUserLanguage' ), - logEventQueue = $.Callbacks( 'memory once' ); + currentLang = mw.config.get( 'wgUserLanguage' ); mw.uls = mw.uls || {}; mw.uls.previousLanguagesCookie = 'uls-previous-languages'; @@ -47,11 +46,7 @@ mw.uls.changeLanguage = function ( language ) { var uri = new mw.Uri( window.location.href ); - mw.uls.logEvent( { - action: 'language-change', - context: 'interface', - interfaceLanguage: language - }, 500 ).always( function () { + mw.hook( 'mw.uls.interface.language.change' ).fire( language, function () { uri.extend( { setlang: language } ); @@ -170,39 +165,6 @@ return !$.client.test( blacklist, null, true ); } - /** - * Local wrapper for 'mw.eventLog.logEvent' which handles default params - * and ensures the correct schema is loaded. - * - * @param {Object} event Event action and optional fields - * @param {int} [timeout] Fail the request if it is not completed within - * the specified timeout. Can be use for example to log actions that - * cause the browser to navigate to other pages. - * @return {jQuery.Promise} jQuery Promise object for the logging call - * @since 2013.07 - * @see https://meta.wikimedia.org/wiki/Schema:UniversalLanguageSelector - */ - mw.uls.logEvent = function ( event, timeout ) { - // We need to create our own deferred for two reasons: - // - logEvent might not be executed immediately - // - we cannot reject a promise returned by it - // So we proxy the original promises status updates - // and register our timeout if requested (for links). - var deferred = $.Deferred(); - - logEventQueue.add( function () { - mw.eventLog.logEvent( 'UniversalLanguageSelector', event ) - .done( deferred.resolve ) - .fail( deferred.reject ); - } ); - - if ( timeout !== undefined ) { - window.setTimeout( deferred.reject, timeout ); - } - - return deferred.promise(); - }; - /** * Initialize ULS front-end and its i18n. * @@ -224,20 +186,6 @@ return; } - // If EventLogging integration is enabled, set event defaults and make the - // the function call event logging with correct schema. - if ( mw.config.get( 'wgULSEventLogging' ) ) { - mw.loader.using( 'schema.UniversalLanguageSelector', function () { - mw.eventLog.setDefaults( 'UniversalLanguageSelector', { - version: 1, - token: mw.user.id(), - contentLanguage: mw.config.get( 'wgContentLanguage' ), - interfaceLanguage: currentLang - } ); - logEventQueue.fire(); - } ); - } - /* * The 'als' is used in a non-standard way in MediaWiki - * it may be used to represent the Allemanic language, diff --git a/resources/js/ext.uls.inputsettings.js b/resources/js/ext.uls.inputsettings.js index a50ba90a..720d4823 100644 --- a/resources/js/ext.uls.inputsettings.js +++ b/resources/js/ext.uls.inputsettings.js @@ -391,10 +391,7 @@ $moreLanguagesButton.on( 'click', function () { inputSettings.$parent.hide(); - mw.uls.logEvent( { - action: 'more-languages-access', - context: 'ime' - } ); + mw.hook( 'mw.uls.ime.morelanguages' ).fire(); } ); }, diff --git a/resources/js/ext.uls.interface.js b/resources/js/ext.uls.interface.js index dfe37783..28f74a1c 100644 --- a/resources/js/ext.uls.interface.js +++ b/resources/js/ext.uls.interface.js @@ -91,7 +91,7 @@ .click(); } ); } - + mw.hook( 'mw.uls.settings.open' ).fire( 'uls' ); uls.hide(); } ); } @@ -121,11 +121,7 @@ } ); } - mw.uls.logEvent( { - action: 'settings-open', - context: 'uls' - } ); - + mw.hook( 'mw.uls.settings.open' ).fire( 'uls' ); uls.hide(); } ); } @@ -252,10 +248,9 @@ // there wont be multiple event handlers bound to same click. $( 'a.uls-prevlang-link' ).on( 'click.ulstipsy', function ( event ) { event.preventDefault(); - mw.uls.logEvent( { action: 'ui-lang-revert' }, 500 ) - .always( function () { - mw.uls.changeLanguage( event.target.lang ); - } ); + mw.hook( 'mw.uls.language.revert' ).fire( function () { + mw.uls.changeLanguage( event.target.lang ); + } ); } ); tipsyTimer = window.setTimeout( function () { hideTipsy(); @@ -333,10 +328,7 @@ if ( languagesettings ) { if ( !languagesettings.shown ) { - mw.uls.logEvent( { - action: 'settings-open', - context: eventParams && eventParams.source || 'interlanguage' - } ); + mw.hook( 'mw.uls.settings.open' ).fire( eventParams && eventParams.source || 'interlanguage' ); } } else { // Initialize the Language settings window @@ -385,10 +377,7 @@ if ( languagesettings ) { if ( !languagesettings.shown ) { - mw.uls.logEvent( { - action: 'settings-open', - context: eventParams && eventParams.source || 'personal' - } ); + mw.hook( 'mw.uls.settings.open' ).fire( eventParams && eventParams.source || 'personal' ); } } else { mw.loader.using( mw.uls.languageSettingsModules, function () { @@ -407,10 +396,7 @@ if ( uls ) { if ( !uls.shown ) { - mw.uls.logEvent( { - action: 'settings-open', - context: eventParams && eventParams.source || 'personal' - } ); + mw.hook( 'mw.uls.settings.open' ).fire( eventParams && eventParams.source || 'personal' ); } } else { // ULS options that are common to all modes of showing