From 117cc5bf4621d0586b900e4fb92bbb7cbbdbadbb Mon Sep 17 00:00:00 2001 From: Santhosh Thottingal Date: Wed, 14 Aug 2013 18:17:33 +0530 Subject: [PATCH] eventlogger module for ULS Separating event logging out of ULS functionality. Event logging is now loosely coupled with ULS. ULS core modules can emit an event whenever EventLogging is required. The eventlogger module will act on the event if configured. Change-Id: I59dfcfb25c1acb85376b56239f2355ee7c4aff1e --- Resources.php | 5 + UniversalLanguageSelector.hooks.php | 8 +- resources/js/ext.uls.displaysettings.js | 24 ++-- resources/js/ext.uls.eventlogger.js | 181 ++++++++++++++++++++++++ resources/js/ext.uls.ime.js | 4 +- resources/js/ext.uls.init.js | 56 +------- resources/js/ext.uls.inputsettings.js | 5 +- resources/js/ext.uls.interface.js | 30 ++-- 8 files changed, 215 insertions(+), 98 deletions(-) create mode 100644 resources/js/ext.uls.eventlogger.js 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