mw.ViewPageTarget: Refetch token if session expired

* Rephrased visualeditor-savedialog-error-badtoken to emphasise
  that it is the old session that become invalid, not the one
  the user started browsing with since in a different window.
* If the session changed, the user will be asked whether they
  agree to save with this new session instead.
* We explictly update mw.config so that future save attempts
  in the same window compare against the correct environment.
  Without this there are two problems when saving and then
  making a second edit in the same window and saving that:
  - It will bring up the same question again (user A -> user B),
    which is annoying.
  - If the user logged back in again (new session, but for
    user A again) it would silently try with that new token
    without asking, thus saving as user A when the user still
    thinks it switched to user B. It switching back automatically
    is not obvious since we asked them from A->B, so we should
    also ask the other way around.
  This can be reproduced by opending ve-edit logged-in, then
  logging out in a new window, save, confirm anon, save,
  open edit again, log back in in a new window, save open edit
  in the old window, confirm new logged-in, save.

Bug: 50424
Change-Id: Id055eca1886f85aeaf615f645de29898afc0373c
This commit is contained in:
Timo Tijhof 2013-07-12 20:27:35 +02:00
parent 71504c3d15
commit 45c79f8c23
3 changed files with 110 additions and 12 deletions

View file

@ -141,7 +141,9 @@ $messages['en'] = array(
'visualeditor-reference-search-reuse' => 'Use an existing source',
'visualeditor-referencelist-isempty' => 'There are no references with the group "$1" on this page.',
'visualeditor-referencelist-missingref' => 'This reference is defined in a template or other generated block, and for now can only be edited in source mode.',
'visualeditor-savedialog-error-badtoken' => 'We could not process your edit because your session has expired.',
'visualeditor-savedialog-error-badtoken' => 'We could not process your edit because the session was no longer valid.',
'visualeditor-savedialog-identify-anon' => "Do you want to save this page as an anonymous user instead? Your IP address will be recorded in this page's edit history.",
'visualeditor-savedialog-identify-user' => 'You are now logged in as [[User:$1|$1]]. Your edit will be associated with this account if you save this edit.',
'visualeditor-savedialog-label-create' => 'Create page',
'visualeditor-savedialog-label-error' => 'Error',
'visualeditor-savedialog-label-report' => 'Report problem',
@ -426,15 +428,22 @@ See also:
'visualeditor-reference-search-reuse' => 'Label for section heading in results list for using an existing reference.
See also:
* {{msg-mw|Visualeditor-reference-search-create}}',
* {{msg-mw|visualeditor-reference-search-create}}',
'visualeditor-referencelist-isempty' => 'Message that appears in the references list when there are no references on the page of that group.',
'visualeditor-referencelist-missingref' => 'Message that appears in the references list, and as a tooltip on the reference itself, for references that are generated by a template or are otherwise uneditable.
See also:
* {{msg-mw|Visualeditor-dialog-meta-languages-readonlynote}}',
* {{msg-mw|visualeditor-dialog-meta-languages-readonlynote}}',
'visualeditor-savedialog-error-badtoken' => 'Error displayed in the save dialog if saving the edit failed due to an invalid edit token (likely due to the user having logged out in a separate window, or logged in again)',
'visualeditor-savedialog-label-create' => 'Label text for save button when the user is creating a new page.
{{Identical|Create page}}',
'visualeditor-savedialog-identify-anon' => 'Displayed in the save dialog if saving failed because the session expired and the session is now an anonymous user. Warning about IP address being recorded is based on {{mw-msg|anoneditwarning}}
{{format|jquerymsg}}',
'visualeditor-savedialog-identify-user' => 'Displayed in the save dialog if saving failed because the session expired and the session is now for a different user account.
{{format|jquerymsg}}',
'visualeditor-savedialog-label-create' => 'Label text for save button when the user is creating a new page',
'visualeditor-savedialog-label-error' => 'Label in front of a save dialog error sentence, separated by {{msg-mw|colon-separator}}.
{{Identical|Error}}',
'visualeditor-savedialog-label-report' => 'Label for button to trigger report',

View file

@ -204,6 +204,7 @@ $wgResourceModules += array(
'jquery.client',
'jquery.placeholder',
'jquery.visibleText',
'mediawiki.api',
'mediawiki.feedback',
'mediawiki.jqueryMsg',
'mediawiki.notify',
@ -224,6 +225,8 @@ $wgResourceModules += array(
'visualeditor-notification-created',
'visualeditor-notification-restored',
'visualeditor-notification-saved',
'visualeditor-savedialog-identify-anon',
'visualeditor-savedialog-identify-user',
),
),
'ext.visualEditor.base' => $wgVisualEditorResourceTemplate + array(

View file

@ -432,7 +432,9 @@ ve.init.mw.ViewPageTarget.prototype.onSave = function ( html, newid ) {
* @param {Object|null} data API response data
*/
ve.init.mw.ViewPageTarget.prototype.onSaveError = function ( jqXHR, status, data ) {
var editApi;
var api, editApi,
viewPage = this;
this.saveDialogSaveButton.setDisabled( false );
this.$saveDialogLoadingIcon.hide();
@ -487,14 +489,91 @@ ve.init.mw.ViewPageTarget.prototype.onSaveError = function ( jqXHR, status, data
// Handle token errors
if ( data.error && data.error.code === 'badtoken' ) {
this.showMessage(
'api-save-error',
document.createTextNode( mw.msg( 'visualeditor-savedialog-error-badtoken' ) ),
{
wrap: 'error'
}
);
this.saveDialogSaveButton.setDisabled( true );
api = new mw.Api();
viewPage.saveDialogSaveButton.setDisabled( true );
viewPage.$saveDialogLoadingIcon.show();
api.get( {
// action=query&meta=userinfo and action=tokens&type=edit can't be combined
// but action=query&meta=userinfo and action=query&prop=info can, however
// that means we have to give it titles and deal with page ids.
'action': 'query',
'meta': 'userinfo',
'prop': 'info',
// Try to send the normalised form so that it is less likely we get extra data like
// data.normalised back that we don't need.
'titles': new mw.Title( viewPage.pageName ).toText(),
'indexpageids': '',
'intoken': 'edit'
} )
.always( function () {
viewPage.$saveDialogLoadingIcon.hide();
} )
.done( function ( data ) {
var badTokenText, userMsg,
userInfo = data.query && data.query.userinfo,
pageInfo = data.query && data.query.pages && data.query.pageids &&
data.query.pageids[0] && data.query.pages[ data.query.pageids[0] ],
editToken = pageInfo && pageInfo.edittoken;
if ( userInfo && editToken ) {
viewPage.editToken = editToken;
if (
( mw.user.isAnon() && userInfo.anon !== undefined ) ||
// Comparing id instead of name to pretect against possible
// normalisation and against case where the user got renamed.
mw.config.get( 'wgUserId' ) === userInfo.id
) {
// New session is the same user still
viewPage.saveDocument();
} else {
// The now current session is a different user
viewPage.saveDialogSaveButton.setDisabled( false );
// Trailing space is to separate from the other message.
badTokenText = document.createTextNode( mw.msg( 'visualeditor-savedialog-error-badtoken' ) + ' ' );
if ( userInfo.anon !== undefined ) {
// New session is an anonymous user
mw.config.set( {
// wgUserId is unset for anonymous users, not set to null
'wgUserId': undefined,
// wgUserName is explicitly set to null for anonymous users,
// functions like mw.user.isAnon rely on this.
'wgUserName': null
} );
viewPage.showMessage(
'api-save-error',
$( badTokenText ).add(
$.parseHTML( mw.message( 'visualeditor-savedialog-identify-anon' ).parse() )
),
{ wrap: 'warning' }
);
} else {
// New session is a different user
mw.config.set( { 'wgUserId': userInfo.id, 'wgUserName': userInfo.name } );
// mediawiki.jqueryMsg has a bug with [[User:$1|$1]] (bug 51388)
userMsg = 'visualeditor-savedialog-identify-user---' + userInfo.name;
mw.messages.set(
userMsg,
mw.messages.get( 'visualeditor-savedialog-identify-user' )
.replace( /\$1/g, userInfo.name )
);
viewPage.showMessage(
'api-save-error',
$( badTokenText ).add(
$.parseHTML( mw.message( userMsg ).parse() )
),
{ wrap: 'warning' }
);
}
}
}
} );
return;
}
@ -767,6 +846,13 @@ ve.init.mw.ViewPageTarget.prototype.onSaveDialogReviewButtonClick = function ()
* @method
*/
ve.init.mw.ViewPageTarget.prototype.onSaveDialogSaveButtonClick = function () {
this.saveDocument();
};
/**
* Try to save the current document.
*/
ve.init.mw.ViewPageTarget.prototype.saveDocument = function () {
var doc = this.surface.getModel().getDocument(),
saveOptions = this.getSaveOptions();