jenkins-bot has submitted this change and it was merged.
Change subject: mw.ViewPageTarget: Show save errors in save dialog instead of
alert
......................................................................
mw.ViewPageTarget: Show save errors in save dialog instead of alert
To trigger a save rejection from the api, set:
$wgSpamRegex = '/spam/i';
and making an edit adding the word "spam" to a page.
Class changes:
* Rename message system in the save dialog from "warning" to
"message" as it will now contain both warnings and errors.
(css class, class property, method names, ..)
Localisation:
* Remove ugly hardcoded and wikitext-requiring "'''Warning:'''"
prefix from the warning message, instead have a message for the
word "Warning" and re-use this in #showMessage for each message
of type "warning" (bolding applied in code instead of in i18n).
* Rename visualeditor-savedialog-dirtywarning to
visualeditor-savedialog-warning-dirty and remove from
VisualEditorMessagesModule.php as it no longer requires pre-
processing from the server.
Clean up:
* Re-alphabetise the order of some messages.
* Clean up duplication and redundant logic in mw.Target#onSaveError
and mw.ViewPageTarget#onSaveError.
Bug: 50350
Change-Id: I3daf631fb0d62ba88e05aa50c77c9940d61395a0
---
M VisualEditor.i18n.php
M VisualEditor.php
M VisualEditorMessagesModule.php
M modules/ve/init/mw/styles/ve.init.mw.ViewPageTarget.css
M modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
M modules/ve/init/mw/ve.init.mw.Target.js
6 files changed, 104 insertions(+), 59 deletions(-)
Approvals:
Catrope: Looks good to me, approved
jenkins-bot: Verified
diff --git a/VisualEditor.i18n.php b/VisualEditor.i18n.php
index 59b3917..b6f3501 100644
--- a/VisualEditor.i18n.php
+++ b/VisualEditor.i18n.php
@@ -123,23 +123,25 @@
'visualeditor-outline-control-move-down' => 'Move item down',
'visualeditor-outline-control-move-up' => 'Move item up',
'visualeditor-preference-enable' => 'Enable VisualEditor (only in the
[[{{MediaWiki:Visualeditor-mainnamespacepagelink}}|main]] and
[[{{MediaWiki:Visualeditor-usernamespacepagelink}}|user]] namespaces)',
- '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 cannot yet be edited with
VisualEditor.',
'visualeditor-reference-input-placeholder' => 'What do you want to
reference?',
'visualeditor-reference-search-create' => 'Create new source',
'visualeditor-reference-search-reuse' => 'Use an existing source',
- 'visualeditor-savedialog-dirtywarning' => "'''Warning:''' Your edit may
have been corrupted – please review before saving.",
+ '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 cannot yet be edited with
VisualEditor.',
'visualeditor-savedialog-label-create' => 'Create page',
+ 'visualeditor-savedialog-label-error' => 'Error',
'visualeditor-savedialog-label-report' => 'Report problem',
'visualeditor-savedialog-label-resolve-conflict' => 'Resolve conflict',
'visualeditor-savedialog-label-restore' => 'Restore page',
'visualeditor-savedialog-label-review' => 'Review your changes',
'visualeditor-savedialog-label-review-good' => 'Return to save form',
'visualeditor-savedialog-label-save' => 'Save page',
+ 'visualeditor-savedialog-label-warning' => 'Warning',
'visualeditor-savedialog-title-conflict' => 'Conflict',
'visualeditor-savedialog-title-nochanges' => 'No changes',
'visualeditor-savedialog-title-review' => 'Review your changes',
'visualeditor-savedialog-title-save' => 'Save your changes',
+ 'visualeditor-savedialog-warning-dirty' => 'Your edit may have been
corrupted – please review before saving.',
'visualeditor-saveerror' => 'Error saving data to server: $1.',
'visualeditor-serializeerror' => 'Error loading data from server: $1.',
'visualeditor-toolbar-cancel' => 'Cancel',
@@ -379,6 +381,7 @@
See also:
* {{msg-mw|Visualeditor-reference-search-create}}',
'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}}.',
'visualeditor-savedialog-label-report' => 'Label for button to trigger
report',
'visualeditor-savedialog-label-resolve-conflict' => 'Label for button
to start resoliving an edit conflict',
'visualeditor-savedialog-label-restore' => 'Label text for save button
when the user is editing a previous revision',
@@ -386,6 +389,7 @@
'visualeditor-savedialog-label-review-good' => 'Label for button to go
back to the save form',
'visualeditor-savedialog-label-save' => 'Label text for save button
when the user is editing a current revision of an extant page.
{{Identical|Save page}}',
+ 'visualeditor-savedialog-label-warning' => 'Label in front of a save
dialog warning sentence, separated by {{msg-mw|colon-separator}}.',
'visualeditor-savedialog-title-conflict' => 'Title for save dialog
slide if there is an edit conflict',
'visualeditor-savedialog-title-nochanges' => 'Title for save dialog
slide for the wikitext diff if there are no changes',
'visualeditor-savedialog-title-review' => 'Title for save dialog slide
for the wikitext diff',
diff --git a/VisualEditor.php b/VisualEditor.php
index e044e2a..5641dfa 100644
--- a/VisualEditor.php
+++ b/VisualEditor.php
@@ -608,22 +608,25 @@
'visualeditor-outline-control-move-down',
'visualeditor-outline-control-move-up',
'visualeditor-outline-control-move-up',
- 'visualeditor-referencelist-isempty',
- 'visualeditor-referencelist-missingref',
'visualeditor-reference-input-placeholder',
'visualeditor-reference-search-create',
'visualeditor-reference-search-reuse',
+ 'visualeditor-referencelist-isempty',
+ 'visualeditor-referencelist-missingref',
'visualeditor-savedialog-label-create',
+ 'visualeditor-savedialog-label-error',
'visualeditor-savedialog-label-report',
'visualeditor-savedialog-label-resolve-conflict',
'visualeditor-savedialog-label-restore',
'visualeditor-savedialog-label-review',
'visualeditor-savedialog-label-review-good',
'visualeditor-savedialog-label-save',
+ 'visualeditor-savedialog-label-warning',
'visualeditor-savedialog-title-conflict',
'visualeditor-savedialog-title-nochanges',
'visualeditor-savedialog-title-review',
'visualeditor-savedialog-title-save',
+ 'visualeditor-savedialog-warning-dirty',
'visualeditor-saveerror',
'visualeditor-serializeerror',
'visualeditor-toolbar-cancel',
diff --git a/VisualEditorMessagesModule.php b/VisualEditorMessagesModule.php
index 71c16b0..f7a4743 100644
--- a/VisualEditorMessagesModule.php
+++ b/VisualEditorMessagesModule.php
@@ -46,7 +46,6 @@
'visualeditor-browserwarning' => array(
'visualeditor-browserwarning' ),
'visualeditor-report-notice' => array(
'visualeditor-report-notice' ),
'missingsummary' => array( 'missingsummary' ),
- 'visualeditor-savedialog-dirtywarning' => array(
'visualeditor-savedialog-dirtywarning' ),
);
// Override message value
diff --git a/modules/ve/init/mw/styles/ve.init.mw.ViewPageTarget.css
b/modules/ve/init/mw/styles/ve.init.mw.ViewPageTarget.css
index 72fe6aa..783e10f 100644
--- a/modules/ve/init/mw/styles/ve.init.mw.ViewPageTarget.css
+++ b/modules/ve/init/mw/styles/ve.init.mw.ViewPageTarget.css
@@ -268,7 +268,7 @@
margin-bottom: 1em;
}
-.ve-init-mw-viewPageTarget-saveDialog-warnings,
+.ve-init-mw-viewPageTarget-saveDialog-messages,
.ve-init-mw-viewPageTarget-saveDialog-conflict,
.ve-init-mw-viewPageTarget-saveDialog-nochanges {
font-size: 0.8em;
diff --git a/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
b/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
index 3d6df5f..b78fffd 100644
--- a/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
+++ b/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
@@ -87,7 +87,7 @@
this.actFromPopState = false;
this.scrollTop = null;
this.currentUri = currentUri;
- this.warnings = {};
+ this.messages = {};
this.restoring = this.oldid !== mw.config.get( 'wgCurRevisionId' );
this.section = currentUri.query.vesection || null;
this.namespaceName = mw.config.get( 'wgCanonicalNamespace' );
@@ -245,7 +245,7 @@
for="ve-init-mw-viewPageTarget-saveDialog-watchList"></label>\
<label
class="ve-init-mw-viewPageTarget-saveDialog-editSummaryCount"></label>\
</div>\
- <div
class="ve-init-mw-viewPageTarget-saveDialog-warnings"></div>\
+ <div
class="ve-init-mw-viewPageTarget-saveDialog-messages"></div>\
<div
class="ve-init-mw-viewPageTarget-saveDialog-actions">\
<div
class="ve-init-mw-viewPageTarget-saveDialog-dirtymsg"></div>\
<div
class="ve-init-mw-viewPageTarget-saveDialog-working"></div>\
@@ -466,25 +466,41 @@
* @param {Object|null} data API response data
*/
ve.init.mw.ViewPageTarget.prototype.onSaveError = function ( jqXHR, status,
data ) {
+ var editApi;
this.saveDialogSaveButton.setDisabled( false );
this.$saveDialogLoadingIcon.hide();
- this.clearWarning( 'captcha' );
+ this.clearMessage( 'api-save-error' );
- // Captcha "errors" usually aren't errors. We simply don't know about
them ahead
- // of time, so we save once, then (if required) we get a captcha back
and try again
- // with captcha.
- // TODO: ConfirmEdit API is horrible, there is no reliable way to know
whether
- // it is a "math", "question" or "fancy" type of captcha. They all
expose differently
- // named properties in the API for different things. At this point we
only support
- // the FancyCaptha which we very intuitively detect by the presence of
a "url" property.
- if ( data.edit && data.edit.captcha && data.edit.captcha.url ) {
+ // Handle empty response
+ if ( !data ) {
+ this.showMessage(
+ 'api-save-error',
+ ve.msg( 'visualeditor-saveerror', 'Empty server
response' ),
+ {
+ wrap: 'error'
+ }
+ );
+ this.saveDialogSaveButton.setDisabled( true );
+ return;
+ }
+
+ // Handle captcha
+ // Captcha "errors" usually aren't errors. We simply don't know about
them ahead of time,
+ // so we save once, then (if required) we get an error with a captcha
back and try again after
+ // the user solved the captcha.
+ // TODO: ConfirmEdit API is horrible, there is no reliable way to know
whether it is a "math",
+ // "question" or "fancy" type of captcha. They all expose differently
named properties in the
+ // API for different things in the UI. At this point we only support
the FancyCaptha which we
+ // very intuitively detect by the presence of a "url" property.
+ editApi = data && data.visualeditor && data.visualeditor.edit;
+ if ( editApi && editApi.captcha && editApi.captcha.url ) {
this.captcha = {
input: new ve.ui.TextInputWidget(),
- id: data.edit.captcha.id
+ id: editApi.captcha.id
};
- this.showWarning(
- 'captcha',
+ this.showMessage(
+ 'api-save-error',
$( '<div>').append(
// msg: simplecaptcha-edit, fancycaptcha-edit,
..
$( '<p>' ).append(
@@ -493,7 +509,7 @@
$( $.parseHTML( mw.message(
'fancycaptcha-edit' ).parse() ) )
.filter( 'a' ).attr( 'target',
'_blank ' ).end()
),
- $( '<img>' ).attr( 'src', data.edit.captcha.url
),
+ $( '<img>' ).attr( 'src', editApi.captcha.url ),
this.captcha.input.$
),
{
@@ -503,8 +519,15 @@
return;
}
- // TODO: Don't use alert.
- alert( ve.msg( 'visualeditor-saveerror', status ) );
+ // Handle (other) unknown and/or unrecoverable errors
+ this.showMessage(
+ 'api-save-error',
+ document.createTextNode( data.error && ( data.error.info ||
data.error.code ) || 'Invalid error code' ),
+ {
+ wrap: 'error'
+ }
+ );
+ this.saveDialogSaveButton.setDisabled( true );
};
/**
@@ -776,16 +799,21 @@
// reset save start and any old captcha data
this.saveStart = +new Date();
if ( this.captcha ) {
- this.clearWarning( 'captcha' );
+ this.clearMessage( 'captcha' );
delete this.captcha;
}
if (
+mw.user.options.get( 'forceeditsummary' ) &&
saveOptions.summary === '' &&
- !this.warnings.missingsummary
+ !this.messages.missingsummary
) {
- this.showWarning( 'missingsummary',
ve.init.platform.getParsedMessage( 'missingsummary' ) );
+ this.showMessage(
+ 'missingsummary',
+ // Wrap manually since this core message already
includes a bold "Warning:" label
+ $( '<p>' ).append( ve.init.platform.getParsedMessage(
'missingsummary' ) ),
+ { wrap: false }
+ );
} else {
this.saveDialogSaveButton.setDisabled( true );
this.$saveDialogLoadingIcon.show();
@@ -1690,12 +1718,14 @@
.not( $slide )
.hide();
- // Old warnings should not persist after slide changes
- this.clearAllWarnings();
+ // Old messages should not persist after slide changes
+ this.clearAllMessages();
+ // Reset save button if we disabled it for e.g. unrecoverable spam error
+ this.saveDialogSaveButton.setDisabled( false );
if ( slide === 'save' ) {
if ( !this.sanityCheckVerified ) {
- this.showWarning( 'dirtywarning',
ve.init.platform.getParsedMessage( 'visualeditor-savedialog-dirtywarning' ) );
+ this.showMessage( 'dirtywarning', mw.msg(
'visualeditor-savedialog-warning-dirty' ) );
}
}
@@ -2117,48 +2147,56 @@
};
/**
- * Show an inline warning.
- * @param {string} name Warning's unique name
- * @param {string|jQuery} message Warning message (string of HTML, not text,
or jQuery object)
+ * Show a message in the save dialog.
+ *
+ * @param {string} name Message's unique name
+ * @param {string|jQuery} message Message content (string of HTML or jQuery
object)
* @param {Object} [options]
- * @param {boolean} [options.wrap=true] Wrap the message in a paragraph.
+ * @param {boolean} [options.wrap="warning"] Whether to wrap the message in a
paragraph and if
+ * so, how. One of "warning", "error" or false.
*/
-ve.init.mw.ViewPageTarget.prototype.showWarning = function ( name, message,
options ) {
- var $warning;
- if ( !this.warnings[name] ) {
+ve.init.mw.ViewPageTarget.prototype.showMessage = function ( name, message,
options ) {
+ var $message;
+ if ( !this.messages[name] ) {
options = options || {};
- $warning = $( '<div
class="ve-init-mw-viewPageTarget-saveDialog-warning"></div>' );
+ $message = $( '<div
class="ve-init-mw-viewPageTarget-saveDialog-message"></div>' );
if ( options.wrap !== false ) {
- $warning.append( $( '<p>').append( message ) );
+ $message.append( $( '<p>').append(
+ // visualeditor-savedialog-label-error
+ // visualeditor-savedialog-label-warning
+ $( '<strong>' ).text( mw.msg(
'visualeditor-savedialog-label-' + options.wrap ) ),
+ document.createTextNode( mw.msg(
'colon-separator' ) ),
+ message
+ ) );
} else {
- $warning.append( message );
+ $message.append( message );
}
- this.$saveDialog.find(
'.ve-init-mw-viewPageTarget-saveDialog-warnings' )
- .append( $warning );
+ this.$saveDialog.find(
'.ve-init-mw-viewPageTarget-saveDialog-messages' )
+ .append( $message );
- this.warnings[name] = $warning;
+ this.messages[name] = $message;
}
};
/**
- * Remove an inline warning.
- * @param {string} name Warning's unique name
+ * Remove a message from the save dialog.
+ * @param {string} name Message's unique name
*/
-ve.init.mw.ViewPageTarget.prototype.clearWarning = function ( name ) {
- if ( this.warnings[name] ) {
- this.warnings[name].remove();
- delete this.warnings[name];
+ve.init.mw.ViewPageTarget.prototype.clearMessage = function ( name ) {
+ if ( this.messages[name] ) {
+ this.messages[name].remove();
+ delete this.messages[name];
}
};
/**
- * Remove all inline warnings.
+ * Remove all messages from the save dialog.
*/
-ve.init.mw.ViewPageTarget.prototype.clearAllWarnings = function () {
+ve.init.mw.ViewPageTarget.prototype.clearAllMessages = function () {
this.$saveDialog
- .find( '.ve-init-mw-viewPageTarget-saveDialog-warnings' )
+ .find( '.ve-init-mw-viewPageTarget-saveDialog-messages' )
.empty();
- this.warnings = {};
+ this.messages = {};
};
/**
diff --git a/modules/ve/init/mw/ve.init.mw.Target.js
b/modules/ve/init/mw/ve.init.mw.Target.js
index 5735026..958c28a 100644
--- a/modules/ve/init/mw/ve.init.mw.Target.js
+++ b/modules/ve/init/mw/ve.init.mw.Target.js
@@ -313,21 +313,22 @@
this.saving = false;
var data = response.visualeditor;
if ( !data && !response.error ) {
- ve.init.mw.Target.onSaveError.call( this, null, 'Invalid
response from server', null );
+ ve.init.mw.Target.onSaveError.call( this, null, 'Invalid
response from server', response );
} else if ( response.error ) {
if ( response.error.code === 'editconflict' ) {
this.emit( 'editConflict' );
} else {
- ve.init.mw.Target.onSaveError.call(
- this, null, 'Unsuccessful request: ' +
response.error.info, null
- );
+ ve.init.mw.Target.onSaveError.call( this, null, 'Save
failure', response );
}
} else if ( data.result !== 'success' ) {
// Note, this could be any of db failure, hookabort, badtoken
or even a captcha
- ve.init.mw.Target.onSaveError.call( this, null, 'Save failure',
data );
+ ve.init.mw.Target.onSaveError.call( this, null, 'Save failure',
response );
} else if ( typeof data.content !== 'string' ) {
ve.init.mw.Target.onSaveError.call(
- this, null, 'Invalid HTML content in response from
server', null
+ this,
+ null,
+ 'Invalid HTML content in response from server',
+ response
);
} else {
mw.config.set( 'wgCurRevisionId', data.newrevid );
--
To view, visit https://gerrit.wikimedia.org/r/71293
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I3daf631fb0d62ba88e05aa50c77c9940d61395a0
Gerrit-PatchSet: 4
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Krinkle <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits