Matthias Mullie has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/326134 )

Change subject: [WIP] Simplify error handling
......................................................................

[WIP] Simplify error handling

This uses the new API error message formats to i18n the message.
This way, we no longer have to try to i18n on the clientside,
which was problematic in some cases since we didn't have all
params.
I'm also getting rid of all api-error-* & related messages in
i18n. There's no point in building messages ourselves, let alone
keep copies of those same messages.

Bug: T115946
Bug: T152607
Change-Id: I58b9a7ed054ad0084bcd7180d92bd69ccc7ed129
---
M resources/handlers/mw.ApiUploadFormDataHandler.js
M resources/handlers/mw.ApiUploadPostHandler.js
M resources/mw.UploadWizard.js
M resources/mw.UploadWizardDetails.js
M resources/mw.UploadWizardUpload.js
M resources/mw.UploadWizardUploadInterface.js
M resources/transports/mw.FirefoggTransport.js
M resources/transports/mw.FormDataTransport.js
M resources/uw.EventFlowLogger.js
M resources/uw.FieldLayout.js
10 files changed, 139 insertions(+), 155 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/UploadWizard 
refs/changes/34/326134/1

diff --git a/resources/handlers/mw.ApiUploadFormDataHandler.js 
b/resources/handlers/mw.ApiUploadFormDataHandler.js
index 57a30df..a11b71a 100644
--- a/resources/handlers/mw.ApiUploadFormDataHandler.js
+++ b/resources/handlers/mw.ApiUploadFormDataHandler.js
@@ -67,19 +67,19 @@
                                                        
handler.upload.setTransportProgress( fraction );
                                                }
                                        } ).then( function ( result ) {
-                                               if ( !result || result.error || 
( result.upload && result.upload.warnings ) ) {
+                                               if ( !result || result.errors 
|| ( result.upload && result.upload.warnings ) ) {
                                                        
uw.eventFlowLogger.logApiError( 'file', result );
                                                }
                                                handler.upload.setTransported( 
result );
                                        }, function ( result ) {
-                                               if ( !result || result.error || 
( result.upload && result.upload.warnings ) ) {
+                                               if ( !result || result.errors 
|| ( result.upload && result.upload.warnings ) ) {
                                                        
uw.eventFlowLogger.logApiError( 'file', result );
                                                }
                                                handler.upload.setTransported( 
result );
                                        } );
                        }, function ( code, info, result ) {
                                uw.eventFlowLogger.logApiError( 'file', result 
);
-                               handler.upload.setError( code, info );
+                               handler.upload.setError( code, info.errors[ 0 
].html );
                        } );
                }
        };
diff --git a/resources/handlers/mw.ApiUploadPostHandler.js 
b/resources/handlers/mw.ApiUploadPostHandler.js
index 49b0084..f648f0a 100644
--- a/resources/handlers/mw.ApiUploadPostHandler.js
+++ b/resources/handlers/mw.ApiUploadPostHandler.js
@@ -27,7 +27,7 @@
                        } )
                                .fail( function ( code, info, result ) {
                                        uw.eventFlowLogger.logApiError( 'file', 
result );
-                                       handler.upload.setError( code, info );
+                                       handler.upload.setError( code, 
info.errors[ 0 ].html );
                                } )
                                .done( function ( result ) {
                                        if ( !result || result.error || ( 
result.upload && result.upload.warnings ) ) {
diff --git a/resources/mw.UploadWizard.js b/resources/mw.UploadWizard.js
index 616092c..4db1ce8 100644
--- a/resources/mw.UploadWizard.js
+++ b/resources/mw.UploadWizard.js
@@ -6,7 +6,7 @@
        mw.UploadWizard = function ( config ) {
                var maxSimPref, wizard = this;
 
-               this.api = new mw.Api( { ajax: { timeout: 0 } } );
+               this.api = this.getApi( { ajax: { timeout: 0 } } );
 
                // making a sort of global for now, should be done by passing 
in config or fragments of config
                // when needed elsewhere
@@ -104,6 +104,66 @@
                },
 
                /**
+                * mw.Api's ajax calls are not very consistent in their error 
handling.
+                * As long as the response comes back, the response will be 
fine: it'll
+                * get rejected with the error details there. However, if no 
response
+                * comes back for whatever reason, things can get confusing.
+                * I'll monkeypatch around such cases so that we can always 
rely on the
+                * error response the way we want it to be.
+                *
+                * @param {Object} options
+                */
+               getApi: function ( options ) {
+                       var api = new mw.Api( options );
+
+                       api.ajax = function ( parameters, ajaxOptions ) {
+                               $.extend( parameters, {
+                                       errorformat: 'html',
+                                       uselang: mw.config.get( 
'wgUserLanguage' ),
+                                       formatversion: 2
+                               } );
+
+                               return mw.Api.prototype.ajax.apply( this, [ 
parameters, ajaxOptions ] ).then(
+                                       null, // done handler - doesn't need 
overriding
+                                       function ( code, info, result ) { // 
fail handler
+                                               var response = {
+                                                       errors: [ {
+                                                               code: code,
+                                                               html: 'unknown'
+                                                       } ]
+                                               };
+
+                                               if ( info && info.errors && 
info.errors[ 0 ] ) {
+                                                       // in case of 
success-but-has-errors, we have a valid result
+                                                       response = info;
+                                               } else if ( info && 
info.textStatus ) {
+                                                       // in case of 
$.ajax.fail(), info will be an object literal
+                                                       // with this data
+                                                       response.errors[ 0 
].html = info.textStatus;
+
+                                                       if ( info.textStatus 
=== 'timeout' ) {
+                                                               
response.errors[ 0 ].html = mw.message( 'timeout' ).text();
+                                                       }
+                                               } else if ( info instanceof 
String ) {
+                                                       // in case of empty 
response with 200 status code, info will
+                                                       // be a string
+                                                       response.errors[ 0 
].html = info;
+                                               }
+
+                                               if ( code === 'http' && result 
&& result.xhr && result.xhr.status === 0 ) {
+                                                       // Failed to even 
connect to server
+                                                       code = 'offline';
+                                               }
+
+                                               return $.Deferred().reject( 
code, response, result );
+                                       }
+                               );
+                       };
+
+                       return api;
+               },
+
+               /**
                 * Helper function to check whether the upload process is 
totally
                 * complete and we can safely leave the window.
                 */
diff --git a/resources/mw.UploadWizardDetails.js 
b/resources/mw.UploadWizardDetails.js
index 262808a..8d0c187 100644
--- a/resources/mw.UploadWizardDetails.js
+++ b/resources/mw.UploadWizardDetails.js
@@ -769,11 +769,12 @@
                submitInternal: function ( params ) {
                        var
                                details = this,
-                               apiPromise = 
details.upload.api.postWithEditToken( params );
+                               apiPromise = this.upload.api.postWithEditToken( 
params );
+
                        return apiPromise
                                .then(
                                        function ( result ) {
-                                               if ( !result || result.error || 
( result.upload && result.upload.warnings ) ) {
+                                               if ( !result || result.errors 
|| ( result.upload && result.upload.warnings ) ) {
                                                        
uw.eventFlowLogger.logApiError( 'details', result );
                                                }
                                                return 
details.handleSubmitResult( result, params );
@@ -782,7 +783,7 @@
                                                uw.eventFlowLogger.logApiError( 
'details', result );
                                                details.upload.state = 'error';
                                                details.processError( code, 
info );
-                                               return $.Deferred().reject( 
code, info );
+                                               return $.Deferred().reject( 
code, info, result );
                                        }
                                )
                                .promise( { abort: apiPromise.abort } );
@@ -907,7 +908,7 @@
                /**
                 * Create a recoverable error -- show the form again, and 
highlight the problematic field.
                 *
-                * @param {mw.Message} errorMessage Error message to show.
+                * @param {string} errorMessage Error message to show.
                 * @param {string} errorCode
                 */
                recoverFromError: function ( errorMessage, errorCode ) {
@@ -921,12 +922,12 @@
                 * Show error state, possibly using a recoverable error form
                 *
                 * @param {string} code Error code
-                * @param {string} statusLine Status line
+                * @param {string} html Error html
                 */
-               showError: function ( code, statusLine ) {
-                       uw.eventFlowLogger.logError( 'details', { code: code, 
message: statusLine } );
+               showError: function ( code, html ) {
+                       uw.eventFlowLogger.logError( 'details', { code: code, 
message: html } );
                        this.showIndicator( 'error' );
-                       this.setStatus( statusLine );
+                       this.setStatus( html );
                },
 
                /**
@@ -936,37 +937,13 @@
                 * @param {Mixed} result Result from ajax call
                 */
                processError: function ( code, result ) {
-                       var statusKey, comma, promise,
-                               statusLine = mw.message( 
'api-error-unclassified' ).text(),
-                               titleBlacklistMessageMap = {
-                                       senselessimagename: 
'senselessimagename', // TODO This is probably never hit?
-                                       'titleblacklist-custom-filename': 
'hosting',
-                                       'titleblacklist-custom-SVG-thumbnail': 
'thumbnail',
-                                       'titleblacklist-custom-thumbnail': 
'thumbnail',
-                                       
'titleblacklist-custom-double-apostrophe': 'double-apostrophe'
-                               },
-                               titleErrorMap = {
-                                       'fileexists-shared-forbidden': 
'fileexists-shared-forbidden',
-                                       protectedpage: 'protected'
-                               };
-
                        if ( code === 'badtoken' ) {
                                this.api.badToken( 'csrf' );
                                // TODO Automatically try again instead of 
requiring the user to bonk the button
                        }
 
                        if ( code === 'abusefilter-disallowed' || code === 
'abusefilter-warning' || code === 'spamblacklist' ) {
-                               // 'amenableparser' will expand templates and 
parser functions server-side.
-                               // We still do the rest of wikitext parsing 
here (throught jqueryMsg).
-                               promise = this.api.loadMessagesIfMissing( [ 
result.error.message.key ], { amenableparser: true } );
-                               this.recoverFromError( mw.message( 'api-error-' 
+ code, function () {
-                                       promise.done( function () {
-                                               mw.errorDialog( $( '<div>' 
).msg(
-                                                       
result.error.message.key,
-                                                       
result.error.message.params
-                                               ) );
-                                       } );
-                               } ), code );
+                               this.recoverFromError( result.errors[ 0 ].html, 
code );
                                return;
                        }
 
@@ -979,41 +956,18 @@
                                // This could potentially also come up when an 
upload is removed by the user, but in that
                                // case the UI is invisible anyway, so whatever.
                                code = 'ratelimited';
-                       } else if ( code === 'http' && result && result.xhr && 
result.xhr.status === 0 ) {
-                               // Failed to even connect to server
-                               code = 'offline';
                        }
 
-                       if ( result && code ) {
-                               if ( titleErrorMap[ code ] ) {
-                                       this.recoverFromError( mw.message( 
'mwe-upwiz-error-title-' + titleErrorMap[ code ] ), 'title-' + titleErrorMap[ 
code ] );
-                                       return;
-                               } else if ( code === 'titleblacklist-forbidden' 
) {
-                                       this.recoverFromError( mw.message( 
'mwe-upwiz-error-title-' + titleBlacklistMessageMap[ result.error.message.key ] 
), 'title-' + titleBlacklistMessageMap[ result.error.message.key ] );
-                                       return;
-                               } else {
-                                       statusKey = 'api-error-' + code;
-                                       if ( code === 'filetype-banned' && 
result.error.blacklisted ) {
-                                               comma = mw.message( 
'comma-separator' ).text();
-                                               code = 'filetype-banned-type';
-                                               statusLine = mw.message( 
'api-error-filetype-banned-type',
-                                                       
result.error.blacklisted.join( comma ),
-                                                       
result.error.allowed.join( comma ),
-                                                       
result.error.allowed.length,
-                                                       
result.error.blacklisted.length
-                                               ).text();
-                                       } else if ( result.error && 
result.error.info ) {
-                                               statusLine = mw.message( 
statusKey, result.error.info ).text();
-                                       } else {
-                                               statusLine = mw.message( 
statusKey, '[no error info]' ).text();
-                                       }
-                               }
+                       if ( [ 'fileexists-shared-forbidden', 'protectedpage', 
'titleblacklist-forbidden' ].indexOf( code ) > -1 ) {
+                               this.recoverFromError( result.errors[ 0 ].html, 
code );
+                               return;
                        }
-                       this.showError( code, statusLine );
+
+                       this.showError( code, result.errors[ 0 ].html );
                },
 
-               setStatus: function ( s ) {
-                       this.div.find( '.mwe-upwiz-file-status-line' ).text( s 
).show();
+               setStatus: function ( html ) {
+                       this.div.find( '.mwe-upwiz-file-status-line' ).html( 
html ).show();
                },
 
                showIndicator: function ( statusStr ) {
diff --git a/resources/mw.UploadWizardUpload.js 
b/resources/mw.UploadWizardUpload.js
index 2e42aeb..225f26b 100644
--- a/resources/mw.UploadWizardUpload.js
+++ b/resources/mw.UploadWizardUpload.js
@@ -116,15 +116,15 @@
        /**
         * Stop the upload -- we have failed for some reason
         */
-       mw.UploadWizardUpload.prototype.setError = function ( code, info, 
additionalStatus ) {
+       mw.UploadWizardUpload.prototype.setError = function ( code, html, 
$additionalStatus ) {
                if ( this.state === 'aborted' ) {
                        // There's no point in reporting an error anymore.
                        return;
                }
                this.state = 'error';
                this.transportProgress = 0;
-               this.ui.showError( code, info, additionalStatus );
-               uw.eventFlowLogger.logError( 'file', { code: code, message: 
info } );
+               this.ui.showError( code, html, $additionalStatus );
+               uw.eventFlowLogger.logError( 'file', { code: code, message: 
html } );
        };
 
        /**
@@ -142,20 +142,17 @@
         */
        mw.UploadWizardUpload.prototype.setTransported = function ( result ) {
                // default error state
-               var comma, warnCode, promise,
-                       code = 'unknown',
-                       info = 'unknown',
+               var warnCode,
+                       error = result.errors && result.errors[ 0 ] ? 
result.errors[ 0 ] : undefined,
+                       code = error && error.code ? error.code : 'unknown',
+                       html = error && error.html ? error.html : 'unknown',
                        $extra;
 
                if ( this.state === 'aborted' ) {
                        return;
                }
 
-               if ( result.error ) {
-                       // If there was an error, we can't really do anything 
else, so let's get out while we can.
-                       if ( result.error.code ) {
-                               code = result.error.code;
-                       }
+               if ( error ) {
                        if ( code === 'badtoken' ) {
                                this.api.badToken( 'csrf' );
                                // Try again once
@@ -164,45 +161,20 @@
                                        return;
                                }
                        }
-                       if ( code === 'filetype-banned' && 
result.error.blacklisted ) {
-                               code = 'filetype-banned-type';
-                               comma = mw.message( 'comma-separator' ).text();
-                               info = [
-                                       result.error.blacklisted.join( comma ),
-                                       result.error.allowed.join( comma ),
-                                       result.error.allowed.length,
-                                       result.error.blacklisted.length
-                               ];
-                       } else if ( code === 'abusefilter-disallowed' || code 
=== 'abusefilter-warning' || code === 'spamblacklist' ) {
-                               // 'amenableparser' will expand templates and 
parser functions server-side.
-                               // We still do the rest of wikitext parsing 
here (throught jqueryMsg).
-                               promise = this.api.loadMessagesIfMissing( [ 
result.error.message.key ], { amenableparser: true } );
-                               info = [
-                                       function () {
-                                               promise.done( function () {
-                                                       mw.errorDialog( $( 
'<div>' ).msg(
-                                                               
result.error.message.key,
-                                                               
result.error.message.params
-                                                       ) );
-                                               } );
-                                       }
-                               ];
 
-                               if ( code === 'abusefilter-warning' ) {
-                                       $extra = new OO.ui.ButtonWidget( {
-                                               label: mw.message( 
'mwe-upwiz-override' ).text(),
-                                               title: mw.message( 
'mwe-upwiz-override-upload' ).text(),
-                                               flags: 'progressive',
-                                               framed: false
-                                       } ).on( 'click', function () {
-                                               // No need to ignore the error, 
AbuseFilter will only return it once
-                                               this.start();
-                                       }.bind( this ) ).$element;
-                               }
-                       } else if ( result.error.info ) {
-                               info = result.error.info;
+                       if ( code === 'abusefilter-warning' ) {
+                               $extra = new OO.ui.ButtonWidget( {
+                                       label: mw.message( 'mwe-upwiz-override' 
).text(),
+                                       title: mw.message( 
'mwe-upwiz-override-upload' ).text(),
+                                       flags: 'progressive',
+                                       framed: false
+                               } ).on( 'click', function () {
+                                       // No need to ignore the error, 
AbuseFilter will only return it once
+                                       this.start();
+                               }.bind( this ) ).$element;
                        }
-                       this.setError( code, info );
+
+                       this.setError( code, html );
                        return;
                }
 
@@ -227,13 +199,14 @@
                                        default:
                                                // we have an unknown warning, 
so let's say what we know
                                                code = 'unknown-warning';
+                                               html = mw.message( 'api-error-' 
+ code ).text();
                                                if ( typeof 
result.upload.warnings[ warnCode ] === 'string' ) {
                                                        // tack the original 
error code onto the warning info
-                                                       info = warnCode + 
mw.message( 'colon-separator' ).text() + result.upload.warnings[ warnCode ];
+                                                       html += warnCode + 
mw.message( 'colon-separator' ).text() + result.upload.warnings[ warnCode ];
                                                } else {
-                                                       info = 
result.upload.warnings[ warnCode ];
+                                                       html += 
result.upload.warnings[ warnCode ];
                                                }
-                                               this.setError( code, info );
+                                               this.setError( code, html );
                                                break;
                                }
                        }
@@ -244,12 +217,12 @@
                                if ( result.upload.imageinfo ) {
                                        this.setSuccess( result );
                                } else {
-                                       this.setError( 'noimageinfo', info );
+                                       this.setError( 'noimageinfo', html );
                                }
                        } else if ( result.upload && result.upload.result === 
'Warning' ) {
                                throw new Error( 'Your browser got back a 
Warning result from the server. Please file a bug.' );
                        } else {
-                               this.setError( code, info );
+                               this.setError( code, html );
                        }
                }
        };
@@ -307,7 +280,7 @@
                        $extra = $extra.add( uploadDuplicate.$element );
                }
 
-               this.setError( code, [ duplicates.length ], $extra );
+               this.setError( code, mw.message( 'api-error-' + code, 
duplicates.length ).text(), $extra );
        };
 
        /**
diff --git a/resources/mw.UploadWizardUploadInterface.js 
b/resources/mw.UploadWizardUploadInterface.js
index f557105..7c48146 100644
--- a/resources/mw.UploadWizardUploadInterface.js
+++ b/resources/mw.UploadWizardUploadInterface.js
@@ -113,6 +113,15 @@
        };
 
        /**
+        * Set status line directly with html content. Make sure the HTML is 
safe!
+        *
+        * @param {string} html
+        */
+       mw.UploadWizardUploadInterface.prototype.setStatusHtml = function ( 
html ) {
+               $( this.div ).find( '.mwe-upwiz-file-status' ).html( html 
).show();
+       };
+
+       /**
         * Set status line directly with a string
         *
         * @param {string} s
@@ -169,34 +178,12 @@
         * Show that transport has failed
         *
         * @param {string} code Error code from API
-        * @param {string|Object} info Extra info
+        * @param {string} html Error message
         * @param {jQuery} [$additionalStatus]
         */
-       mw.UploadWizardUploadInterface.prototype.showError = function ( code, 
info, $additionalStatus ) {
-               var msgKey, args, moreErrorCodes = [
-                       'unknown-warning',
-                       'abusefilter-disallowed',
-                       'abusefilter-warning',
-                       'spamblacklist',
-                       'offline',
-                       'parsererror'
-               ];
-
+       mw.UploadWizardUploadInterface.prototype.showError = function ( code, 
html, $additionalStatus ) {
                this.showIndicator( 'error' );
-               // is this an error that we expect to have a message for?
-
-               if ( code === 'http' && info.textStatus === 'timeout' ) {
-                       code = 'timeout';
-               }
-
-               if ( $.inArray( code, mw.Api.errors ) !== -1 || $.inArray( 
code, moreErrorCodes ) !== -1 ) {
-                       msgKey = 'api-error-' + code;
-                       args = $.makeArray( info );
-               } else {
-                       msgKey = 'api-error-unknown-code';
-                       args = [ code ].concat( $.makeArray( info ) );
-               }
-               this.setStatus( msgKey, args );
+               this.setStatusHtml( html );
                this.setAdditionalStatus( $additionalStatus );
        };
 
diff --git a/resources/transports/mw.FirefoggTransport.js 
b/resources/transports/mw.FirefoggTransport.js
index e2e76b7..28a3975 100644
--- a/resources/transports/mw.FirefoggTransport.js
+++ b/resources/transports/mw.FirefoggTransport.js
@@ -41,10 +41,10 @@
                                        } else {
                                                // encoding failed
                                                deferred.reject( {
-                                                       error: {
+                                                       errors: [ {
                                                                code: 
'firefogg',
-                                                               info: 'Encoding 
failed'
-                                                       }
+                                                               html: 'Encoding 
failed'
+                                                       } ]
                                                } );
                                        }
                                }, function ( progress ) {
diff --git a/resources/transports/mw.FormDataTransport.js 
b/resources/transports/mw.FormDataTransport.js
index 6208577..74dc987 100644
--- a/resources/transports/mw.FormDataTransport.js
+++ b/resources/transports/mw.FormDataTransport.js
@@ -87,6 +87,11 @@
 
                formData.append( 'offset', offset || 0 );
 
+               // request parsed errors, in user language
+               formData.append( 'errorformat', 'html' );
+               formData.append( 'uselang', mw.config.get( 'wgUserLanguage' ) );
+               formData.append( 'formatversion', 2 );
+
                return formData;
        };
 
@@ -426,11 +431,12 @@
                                // Let's check what caused this, too.
                                window.console.error( 'parsererror', evt );
                        }
+
                        response = {
-                               error: {
-                                       code: evt.target.status !== 0 ? 
'parsererror' : 'offline',
-                                       info: evt.target.responseText
-                               }
+                               errors: [ {
+                                       code: 'parsererror',
+                                       html: mw.message( evt.target.status !== 
0 ? 'api-error-parsererror' : 'api-error-offline' ).text()
+                               } ]
                        };
                }
 
diff --git a/resources/uw.EventFlowLogger.js b/resources/uw.EventFlowLogger.js
index 8802602..c4048dc 100644
--- a/resources/uw.EventFlowLogger.js
+++ b/resources/uw.EventFlowLogger.js
@@ -157,7 +157,11 @@
                var code, message;
                if ( !result ) {
                        code = 'api/empty';
+               } else if ( result.errors ) {
+                       // formatversion=2
+                       code = 'api/error/' + result.errors[0].code;
                } else if ( result.error ) {
+                       // formatversion=1
                        code = 'api/error/' + result.error.code;
                } else if ( result.upload ) {
                        code = 'api/warning/' + Object.keys( 
result.upload.warnings || {} ).sort().join( ',' );
diff --git a/resources/uw.FieldLayout.js b/resources/uw.FieldLayout.js
index 1e24a04..8074f61 100644
--- a/resources/uw.FieldLayout.js
+++ b/resources/uw.FieldLayout.js
@@ -87,7 +87,7 @@
         */
        uw.FieldLayout.prototype.makeMessage = function ( kind, msg ) {
                var
-                       content = msg.parseDom(),
+                       content = msg instanceof mw.Message ? msg.parseDom() : 
msg,
                        $listItem = 
uw.FieldLayout.parent.prototype.makeMessage.call( this, kind, content );
                $listItem.addClass( 'mwe-upwiz-fieldLayout-' + kind + '-' + 
msg.key );
                return $listItem;

-- 
To view, visit https://gerrit.wikimedia.org/r/326134
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I58b9a7ed054ad0084bcd7180d92bd69ccc7ed129
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/UploadWizard
Gerrit-Branch: master
Gerrit-Owner: Matthias Mullie <mmul...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to