Florianschmidtwelzow has uploaded a new change for review. https://gerrit.wikimedia.org/r/207756
Change subject: Initial commit ...................................................................... Initial commit First version of OOJsUIAjaxLogin with basic functionality. Change-Id: I8b32a8482f96908bbdfef2130ecca07e2ed3e5de --- A LICENSE A OOJsUIAjaxLogin.php A i18n/en.json A i18n/qqq.json A includes/OOJsUIAjaxLogin.hooks.php A resources/Resources.php A resources/ext.OOJsUIAjaxLogin.init/init.js A resources/ext.OOJsUIAjaxLogin.overlay/LoginOverlay.js A resources/ext.OOJsUIAjaxLogin.overlay/LoginOverlay.less 9 files changed, 464 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/OOJsUIAjaxLogin refs/changes/56/207756/1 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d37a2f3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Florian Schmidt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/OOJsUIAjaxLogin.php b/OOJsUIAjaxLogin.php new file mode 100644 index 0000000..7be12f7 --- /dev/null +++ b/OOJsUIAjaxLogin.php @@ -0,0 +1,49 @@ +<?php +/** +The MIT License (MIT) + +Copyright (c) 2015 Florian Schmidt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + */ + +if ( !defined( 'MEDIAWIKI' ) ) { + die( 'This is an extension for Mediawiki and can not run standalone.' ); +} + +$wgExtensionCredits['other'][] = array( + 'path' => __FILE__, + 'name' => 'OOJsUIAjaxLogin', + 'author' => 'Florian Schmidt', + 'url' => 'https://www.mediawiki.org/wiki/Extension:OOJsUIAjaxLogin', + 'descriptionmsg' => 'oojsuiajaxlogin-desc', + 'version' => '0.0.1', + 'license-name' => "MIT", +); + +// Autoload Classes +$wgAutoloadClasses['OOJsUIAjaxLoginHooks'] = __DIR__ . '/includes/OOJsUIAjaxLogin.hooks.php'; + +// message dir +$wgMessagesDirs['OOJsUIAjaxLogin'] = __DIR__ . '/i18n'; + +// Hooks +$wgHooks['BeforePageDisplay'][] = 'OOJsUIAjaxLoginHooks::onBeforePageDisplay'; + +require __DIR__ . '/resources/Resources.php'; diff --git a/i18n/en.json b/i18n/en.json new file mode 100644 index 0000000..c44a7f9 --- /dev/null +++ b/i18n/en.json @@ -0,0 +1,9 @@ +{ + "@metadata": { + "authors": [ + "Florian Schmidt" + ] + }, + "oojsuiajaxlogin-desc": "Adds an Ajax Login overlay to MediaWiki.", + "oojsuiajaxlogin-loading": "Loading..." +} diff --git a/i18n/qqq.json b/i18n/qqq.json new file mode 100644 index 0000000..7098ee7 --- /dev/null +++ b/i18n/qqq.json @@ -0,0 +1,9 @@ +{ + "@metadata": { + "authors": [ + "Florian Schmidt" + ] + }, + "oojsuiajaxlogin-desc": "{{doc-special|OOHsUIAjaxLOgin}}", + "oojsuiajaxlogin-loading": "Test for the login link used to indicate, that the Ajax login overlay is loading." +} diff --git a/includes/OOJsUIAjaxLogin.hooks.php b/includes/OOJsUIAjaxLogin.hooks.php new file mode 100644 index 0000000..2e6d80a --- /dev/null +++ b/includes/OOJsUIAjaxLogin.hooks.php @@ -0,0 +1,16 @@ +<?php + +class OOJsUIAjaxLoginHooks { + /** + * BeforePageDisplay hook handler. Add the required module for this extension. + * + * @param OutputPage $out + * @param Skin $sk + */ + public static function onBeforePageDisplay( OutputPage &$out, Skin &$sk ) { + $out->addModules( array( + 'ext.OOJsUIAjaxLogin.init' + ) + ); + } +} diff --git a/resources/Resources.php b/resources/Resources.php new file mode 100644 index 0000000..bad6843 --- /dev/null +++ b/resources/Resources.php @@ -0,0 +1,45 @@ +<?php +// Resource template +$wgResourceTemplate = array( + 'localBasePath' => __DIR__, + 'remoteExtPath' => 'OOJsUIAjaxLogin/resources', +); +// Resource modules +$wgResourceModules += array( + 'ext.OOJsUIAjaxLogin.init' => $wgResourceTemplate + array( + 'scripts' => array( + 'ext.OOJsUIAjaxLogin.init/init.js', + ), + 'messages' => array( + 'oojsuiajaxlogin-loading', + ), + ), + 'ext.OOJsUIAjaxLogin.overlay' => $wgResourceTemplate + array( + 'dependencies' => array( + 'oojs-ui', + 'mediawiki.api', + 'mediawiki.jqueryMsg', + ), + 'scripts' => array( + 'ext.OOJsUIAjaxLogin.overlay/LoginOverlay.js', + ), + 'styles' => array( + 'ext.OOJsUIAjaxLogin.overlay/LoginOverlay.less', + ), + 'messages' => array( + 'login', + 'wrongpassword', + 'userlogin-yourname', + 'userlogin-yourname-ph', + 'userlogin-yourpassword', + 'userlogin-yourpassword-ph', + 'pt-login-button', + 'userlogin-joinproject', + 'cancel', + 'welcomeuser', + 'unknown-error', + ), + ), +); + +unset( $wgResourceTemplate ); diff --git a/resources/ext.OOJsUIAjaxLogin.init/init.js b/resources/ext.OOJsUIAjaxLogin.init/init.js new file mode 100644 index 0000000..125126a --- /dev/null +++ b/resources/ext.OOJsUIAjaxLogin.init/init.js @@ -0,0 +1,24 @@ +( function ( $ ) { + // add click event listener to login link in personal tools + $( '#pt-login a' ).on( 'click', function ( ev ) { + var $el = $( ev.target ), + oldEl; + + // don't navigate to Special:UserLogin + ev.preventDefault(); + // replace login link with a loading text to indicate, that the login form is loading + oldEl = $el.after( + '<span id="oojsui-ajaxlogin">' + mw.msg( 'oojsuiajaxlogin-loading' ) + '</span>' + ).detach(); + // load the overlay module and show the overlay + mw.loader.using( 'ext.OOJsUIAjaxLogin.overlay', function () { + mw.OOJsUIAjaxLogin.show(); + }, function () { + // if the module could not be loaded, navigate to the original target + window.location.href = ev.target.href; + } ).then( function () { + // replace the loading text with the old login link + $( '#oojsui-ajaxlogin' ).replaceWith( oldEl ); + } ); + } ); +} ( jQuery ) ); diff --git a/resources/ext.OOJsUIAjaxLogin.overlay/LoginOverlay.js b/resources/ext.OOJsUIAjaxLogin.overlay/LoginOverlay.js new file mode 100644 index 0000000..1a88ad6 --- /dev/null +++ b/resources/ext.OOJsUIAjaxLogin.overlay/LoginOverlay.js @@ -0,0 +1,286 @@ +( function ( $ ) { + // Create and append a window manager, which will open and close the window. + var windowManager = new OO.ui.WindowManager(); + $( 'body' ).append( windowManager.$element ); + + mw.OOJsUIAjaxLogin = { + /** + * @var {null|OO.ui.MessageDialog} save the created overlay from setup + */ + overlay: false, + /** + * @var {Number} loginRetry How often the login was retried (after 3 times, the login will be aborted) + */ + loginRetry: 0, + /** + * @var {Boolean} loginLock Is there already a login process? + */ + loginLock: false, + + /** + * Setup the login overlay with all required fields, buttons and inputs. + * Resulting Ovleray will be saved in this.overlay. + */ + setup: function () { + var self = this; + + // initialize api to try to login later + this.api = new mw.Api; + + /** + * Constructor for LoginOverlay MessageDialog + * @param {Object} config Configuration parameters + */ + function LoginOverlay( config ) { + LoginOverlay.super.call( this, config ); + } + // inherit MessageDialog to LoginOverlay + OO.inheritClass( LoginOverlay, OO.ui.MessageDialog ); + + // Set title to message from Special:UserLogin + LoginOverlay.static.title = mw.msg( 'login' ); + + // Default actions for this overlay + LoginOverlay.static.actions = [ + { + action: 'login', + label: mw.msg( 'pt-login-button' ), + flags: [ 'primary', 'constructive' ] + }, + { + action: 'register', + label: mw.msg( 'userlogin-joinproject' ), + flags: [ 'progressive' ] + }, + { + action: 'cancel', + label: mw.msg( 'cancel' ), + flags: [ 'safe', 'destructive' ] + } + ]; + + /** + * Handles a click on one of the defined actions for this overlay. + */ + LoginOverlay.prototype.getActionProcess = function ( action ) { + // don't do anything, if there is already a login process + if ( self.loginLock ) { + return new OO.ui.Process( function () { return false }, this ); + } + switch ( action ) { + // login action, try a login with the data provided in the input fields + case 'login': + // lock the login process to don't do a login twice at the same time + self.loginLock = true; + // hide all fields and show the progress bar + resize the dialog + this.fieldset.toggle( false ); + this.loginProgressBar.toggle( true ); + this.updateSize(); + return new OO.ui.Process( self.tryLogin( + this.usernameInput.getValue(), + this.passwordInput.getValue(), + this + ), this ); + break; + case 'register': + // create the login link parameters + var params = { + returnto: mw.config.get( 'wgPageName' ) + }, + signupParams ={ + type: 'signup' + }; + + // navigate to Special:UserLogin/signup + window.location.href = mw.util.getUrl( 'Special:UserLogin', $.extend( params, signupParams ) ); + return; + break; + default: + break; + } + // Fallback to parent handler + return LoginOverlay.super.prototype.getActionProcess.call( this, action ); + }; + + /** + * Initialize the overlay, create input fields and labels and so on. + */ + LoginOverlay.prototype.initialize = function () { + var progressBarFieldset = new OO.ui.FieldsetLayout(); + + // Call the parent method + LoginOverlay.super.prototype.initialize.call( this ); + + // create input fields + this.usernameInput = new OO.ui.TextInputWidget( { + placeholder: mw.msg( 'userlogin-yourname-ph' ) + } ), + this.passwordInput = new OO.ui.TextInputWidget( { + placeholder: mw.msg( 'userlogin-yourpassword-ph' ), + type: 'password' + } ); + // create a field for error messages and hide it by default + this.errorMessageContainer = new OO.ui.LabelWidget( { + label: '', + classes: [ 'errorbox', 'oojsui-ajaxlogin' ] + } ); + this.errorMessageContainer.toggle( false ); + // same for a progress bar + this.loginProgressBar = new OO.ui.ProgressBarWidget(); + this.loginProgressBar.toggle( false ); + + progressBarFieldset.addItems( [ + new OO.ui.FieldLayout( this.loginProgressBar, { + align: 'top' + } ) + ] ); + // Create a fieldset for the input fields. + this.fieldset = new OO.ui.FieldsetLayout(); + + // add input fields to the fieldset + this.fieldset.addItems( [ + new OO.ui.FieldLayout( this.usernameInput, { + label: mw.msg( 'userlogin-yourname' ), + align: 'top' + } ), + new OO.ui.FieldLayout( this.passwordInput, { + label: mw.msg( 'userlogin-yourpassword' ), + align: 'top' + } ), + new OO.ui.FieldLayout( this.errorMessageContainer, { + align: 'top' + } ) + ] ); + + // set the content + this.content = new OO.ui.PanelLayout( { $: this.$, padded: true, expanded: false } ); + this.content.$element.append( + this.title.$element, + this.fieldset.$element, + progressBarFieldset.$element + ); + this.$body.append( this.content.$element ); + }; + + /** + * @inheritdoc + */ + LoginOverlay.prototype.getReadyProcess = function ( data ) { + var self = this; + // make sure, that username input field is focused + return LoginOverlay.super.prototype.getReadyProcess.call( this, data ) + .next( function () { + self.usernameInput.focus(); + } ); + } + // Override the getBodyHeight() method to specify a custom height (or don't to use the automatically generated height) + LoginOverlay.prototype.getBodyHeight = function () { + // get the height of the body and let some place for error messages + return this.content.$element.outerHeight( true ); + }; + + // Create a new LoginOverlay and save it to this.overlay + this.overlay = new LoginOverlay({ + size: 'large' + }); + + // Add the window to the window manager using the addWindows() method + windowManager.addWindows( [ this.overlay ] ); + }, + + /** + * Setup a new overlay (if needed) and show it with the windowManager + */ + show: function () { + if ( !this.overlay ) { + this.setup(); + } + // Open the window! + windowManager.openWindow( this.overlay ); + }, + + /** + * Try to login with the given data through the Api. + * @param {String} username Plain User name + * @param {String} password Plain Password + * @param {LoginOverlay} ov The LoginOverlay object where this function is called from + * @param {String} [token] Optional logintoken provided by the login api + */ + tryLogin: function ( username, password, ov, token ) { + var self = this; + + // make the request + this.api.post( { + action: 'login', + lgname: username, + lgpassword: password, + lgtoken: token + } ).done( function ( data ) { + // check, if this is the 6 try to the login api and if so, don't do anything + if ( self.loginRetry < 6 && data.login.hasOwnProperty( 'result' ) ) { + // check, if the login was successful or handle errors + switch ( data.login.result ) { + case 'NeedToken': + // the first call to the api returns a token to call the Api again to + // really try to login + // increase the login try counter + self.loginRetry++; + // try again, now with the required token + return self.tryLogin( username, password, ov, data.login.token ); + break; + case 'Success': + // The user was logged in successfully, show a welcome message and reload + // this page + var welcomeWidget = label1 = new OO.ui.LabelWidget( { + label: mw.msg( 'welcomeuser', data.login.lgusername ) + } ); + + ov.loginProgressBar.toggle( false ); + ov.content.$element.append( + welcomeWidget.$element + ); + ov.updateSize(); + window.location.reload(); + break; + default: + // the default is: false password, try again + self.loginRetry = 0; + return self.addErrorMessage( ov, mw.msg( 'wrongpassword' ) ); + break; + } + } else { + // navigate to the full login page + var params = { + returnto: mw.config.get( 'wgPageName' ) + }; + + // navigate to Special:UserLogin + window.location.href = mw.util.getUrl( 'Special:UserLogin', params ); + } + } ).fail( function () { + // unknown error + // FIXME: i18n & better error handling + self.addErrorMessage( ov, mw.msg( 'unknown-error' ) ); + } ).always( function () { + // reset loginLock to allow a new login try + self.loginLock = false; + } ); + }, + + /** + * Adds an errorbox to the provided login form with the provided message and turns off any progressbar. + * @param {LoginOverlay} ov LoginOverlay object to show the errorbox on + * @param {String} msg Error text + * @return {boolean} always returns false + */ + addErrorMessage: function ( ov, msg ) { + ov.loginProgressBar.toggle( false ); + ov.fieldset.toggle( true ); + ov.errorMessageContainer.$element.text( msg ); + ov.errorMessageContainer.toggle( true ); + ov.updateSize(); + return false; + } + }; + +} ( jQuery ) ); \ No newline at end of file diff --git a/resources/ext.OOJsUIAjaxLogin.overlay/LoginOverlay.less b/resources/ext.OOJsUIAjaxLogin.overlay/LoginOverlay.less new file mode 100644 index 0000000..d707596 --- /dev/null +++ b/resources/ext.OOJsUIAjaxLogin.overlay/LoginOverlay.less @@ -0,0 +1,5 @@ +// limit all changes to the errorbox to our overlay +.oojsui-ajaxlogin.errorbox { + display: block; + text-align: center; +} -- To view, visit https://gerrit.wikimedia.org/r/207756 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I8b32a8482f96908bbdfef2130ecca07e2ed3e5de Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/OOJsUIAjaxLogin Gerrit-Branch: master Gerrit-Owner: Florianschmidtwelzow <florian.schmidt.wel...@t-online.de> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits