From c0c63a286f82aa07f5584f39cd4c7b56f41d76cc Mon Sep 17 00:00:00 2001 From: =?utf8?q?Bartosz=20Dziewo=C5=84ski?= Date: Thu, 23 Jun 2016 06:09:23 +0200 Subject: [PATCH] mw.Upload.BookletLayout: Better handle error messages from AbuseFilter and TitleBlacklist And hopefully, also other extensions, if they follow the format where result.error.message.key is the key of a message that can be used to report the error to the user and result.error.message.params is an array of parameters for it, if any. The message text is loaded dynamically from the foreign wiki. Bug: T115260 Bug: T137841 Depends-On: I97c1f5c6bbbdfc0b8ea9914bb075d5299c14df8f (TitleBlacklist) Depends-On: I5780eae96930211191ecd874aacf53fdacb58f89 (AbuseFilter) Change-Id: I5d1a289cf3d3b9de53047566172ab19a859e608e --- languages/i18n/en.json | 1 - languages/i18n/qqq.json | 1 - resources/Resources.php | 1 - resources/src/mediawiki/api/messages.js | 19 ++++ .../mediawiki.Upload.BookletLayout.js | 97 +++++++++++-------- 5 files changed, 74 insertions(+), 45 deletions(-) diff --git a/languages/i18n/en.json b/languages/i18n/en.json index 0d7d047705..a35a1765ce 100644 --- a/languages/i18n/en.json +++ b/languages/i18n/en.json @@ -4057,7 +4057,6 @@ "mw-widgets-dateinput-placeholder-month": "YYYY-MM", "mw-widgets-titleinput-description-new-page": "page does not exist yet", "mw-widgets-titleinput-description-redirect": "redirect to $1", - "api-error-blacklisted": "Please choose a different, descriptive title.", "sessionmanager-tie": "Cannot combine multiple request authentication types: $1.", "sessionprovider-generic": "$1 sessions", "sessionprovider-mediawiki-session-cookiesessionprovider": "cookie-based sessions", diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index c1e065219c..79e9cdd513 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -4238,7 +4238,6 @@ "mw-widgets-dateinput-placeholder-month": "Placeholder displayed in a date input field when it's empty, representing a date format with 4 digits for year and 2 digits for month, separated with hyphens (without a day). This should be uppercase, if possible, and must not include any additional explanations. If there is no good way to translate it, make this message blank.", "mw-widgets-titleinput-description-new-page": "Description label for a new page in the title input widget.", "mw-widgets-titleinput-description-redirect": "Description label for a redirect in the title input widget.", - "api-error-blacklisted": "Used as error message.\n\nFollowed by the link {{msg-mw|Mwe-upwiz-feedback-blacklist-info-prompt}}.", "sessionmanager-tie": "Used as an error message when multiple session sources are tied in priority.\n\nParameters:\n* $1 - List of dession type descriptions, from messages like {{msg-mw|sessionprovider-mediawiki-session-cookiesessionprovider}}.", "sessionprovider-generic": "Used to create a generic session type description when one isn't provided via the proper message. Should be phrased to make sense when added to a message such as {{msg-mw|cannotloginnow-text}}.\n\nParameters:\n* $1 - PHP classname.", "sessionprovider-mediawiki-session-cookiesessionprovider": "Description of the sessions provided by the CookieSessionProvider class, which use HTTP cookies. Should be phrased to make sense when added to a message such as {{msg-mw|cannotloginnow-text}}.", diff --git a/resources/Resources.php b/resources/Resources.php index 150b8d319c..2e22c93276 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -1298,7 +1298,6 @@ return [ 'filename-thumb-name', 'badfilename', 'protectedpagetext', - 'api-error-blacklisted', // HACK ], ], 'mediawiki.ForeignStructuredUpload.BookletLayout' => [ diff --git a/resources/src/mediawiki/api/messages.js b/resources/src/mediawiki/api/messages.js index df21eb2812..9ba562e266 100644 --- a/resources/src/mediawiki/api/messages.js +++ b/resources/src/mediawiki/api/messages.js @@ -44,6 +44,25 @@ */ loadMessages: function ( messages ) { return this.getMessages( messages ).then( $.proxy( mw.messages, 'set' ) ); + }, + + /** + * Loads a set of mesages and add them to mw.messages. Only messages that are not already known + * are loaded. If all messages are known, the returned promise is resolved immediately. + * + * @param {Array} messages Messages to retrieve + * @return {jQuery.Promise} + */ + loadMessagesIfMissing: function ( messages ) { + var missing = messages.filter( function ( msg ) { + return !mw.message( msg ).exists(); + } ); + + if ( missing.length === 0 ) { + return $.Deferred().resolve(); + } + + return this.getMessages( missing ).then( $.proxy( mw.messages, 'set' ) ); } } ); diff --git a/resources/src/mediawiki/mediawiki.Upload.BookletLayout.js b/resources/src/mediawiki/mediawiki.Upload.BookletLayout.js index bbd0f1b74e..31e4492d3a 100644 --- a/resources/src/mediawiki/mediawiki.Upload.BookletLayout.js +++ b/resources/src/mediawiki/mediawiki.Upload.BookletLayout.js @@ -260,10 +260,9 @@ layout.emit( 'fileUploaded' ); }, function () { // These errors will be thrown while the user is on the info page. - // Pretty sure it's impossible to get a warning other than 'stashfailed' here, which should - // really be an error... - var errorMessage = layout.getErrorMessageForStateDetails(); - deferred.reject( errorMessage ); + layout.getErrorMessageForStateDetails().then( function ( errorMessage ) { + deferred.reject( errorMessage ); + } ); }, function ( progress ) { var elapsedTime = new Date() - startTime, estimatedTotalTime = ( 1 / progress ) * elapsedTime, @@ -309,8 +308,9 @@ deferred.resolve(); layout.emit( 'fileSaved', layout.upload.getImageInfo() ); }, function () { - var errorMessage = layout.getErrorMessageForStateDetails(); - deferred.reject( errorMessage ); + layout.getErrorMessageForStateDetails().then( function ( errorMessage ) { + deferred.reject( errorMessage ); + } ); } ); } ); @@ -322,7 +322,7 @@ * state and state details. * * @protected - * @return {OO.ui.Error} Error to display for given state and details. + * @return {jQuery.Promise} A Promise that will be resolved with an OO.ui.Error. */ mw.Upload.BookletLayout.prototype.getErrorMessageForStateDetails = function () { var message, @@ -334,21 +334,34 @@ if ( state === mw.Upload.State.ERROR ) { if ( !error ) { // If there's an 'exception' key, this might be a timeout, or other connection problem - return new OO.ui.Error( + return $.Deferred().resolve( new OO.ui.Error( $( '

' ).msg( 'api-error-unknownerror', JSON.stringify( stateDetails ) ), { recoverable: false } - ); + ) ); } - // HACK We should either have a hook here to allow TitleBlacklist to handle this, or just have - // TitleBlacklist produce sane error messages that can be displayed without arcane knowledge - if ( error.info === 'TitleBlacklist prevents this title from being created' ) { - // HACK Apparently the only reliable way to determine whether TitleBlacklist was involved - return new OO.ui.Error( - // HACK TitleBlacklist doesn't have a sensible message, this one is from UploadWizard - $( '

' ).msg( 'api-error-blacklisted' ), - { recoverable: false } - ); + // Errors in this format are produced by TitleBlacklist and AbuseFilter. Perhaps other + // extensions will follow this format in the future. + if ( error.message ) { + return this.upload.getApi() + .then( function ( api ) { + return api.loadMessagesIfMissing( [ error.message.key ] ).then( function () { + if ( !mw.message( error.message.key ).exists() ) { + return $.Deferred().reject(); + } + return new OO.ui.Error( + $( '

' ).msg( error.message.key, error.message.params || [] ), + { recoverable: false } + ); + } ); + } ) + .then( null, function () { + // We failed when loading the error message, or it doesn't actually exist, fall back + return $.Deferred().resolve( new OO.ui.Error( + $( '

' ).msg( 'api-error-unknownerror', JSON.stringify( stateDetails ) ), + { recoverable: false } + ) ); + } ); } if ( error.code === 'protectedpage' ) { @@ -359,10 +372,10 @@ message = mw.message( 'api-error-unknownerror', JSON.stringify( stateDetails ) ); } } - return new OO.ui.Error( + return $.Deferred().resolve( new OO.ui.Error( $( '

' ).append( message.parseDom() ), { recoverable: false } - ); + ) ); } if ( state === mw.Upload.State.WARNING ) { @@ -370,63 +383,63 @@ // of importance. For example fixing the thumbnail like file name // won't help the fact that the file already exists. if ( warnings.stashfailed !== undefined ) { - return new OO.ui.Error( + return $.Deferred().resolve( new OO.ui.Error( $( '

' ).msg( 'api-error-stashfailed' ), { recoverable: false } - ); + ) ); } else if ( warnings.exists !== undefined ) { - return new OO.ui.Error( + return $.Deferred().resolve( new OO.ui.Error( $( '

' ).msg( 'fileexists', 'File:' + warnings.exists ), { recoverable: false } - ); + ) ); } else if ( warnings[ 'exists-normalized' ] !== undefined ) { - return new OO.ui.Error( + return $.Deferred().resolve( new OO.ui.Error( $( '

' ).msg( 'fileexists', 'File:' + warnings[ 'exists-normalized' ] ), { recoverable: false } - ); + ) ); } else if ( warnings[ 'page-exists' ] !== undefined ) { - return new OO.ui.Error( + return $.Deferred().resolve( new OO.ui.Error( $( '

' ).msg( 'filepageexists', 'File:' + warnings[ 'page-exists' ] ), { recoverable: false } - ); + ) ); } else if ( warnings.duplicate !== undefined ) { - return new OO.ui.Error( + return $.Deferred().resolve( new OO.ui.Error( $( '

' ).msg( 'api-error-duplicate', warnings.duplicate.length ), { recoverable: false } - ); + ) ); } else if ( warnings[ 'thumb-name' ] !== undefined ) { - return new OO.ui.Error( + return $.Deferred().resolve( new OO.ui.Error( $( '

' ).msg( 'filename-thumb-name' ), { recoverable: false } - ); + ) ); } else if ( warnings[ 'bad-prefix' ] !== undefined ) { - return new OO.ui.Error( + return $.Deferred().resolve( new OO.ui.Error( $( '

' ).msg( 'filename-bad-prefix', warnings[ 'bad-prefix' ] ), { recoverable: false } - ); + ) ); } else if ( warnings[ 'duplicate-archive' ] !== undefined ) { - return new OO.ui.Error( + return $.Deferred().resolve( new OO.ui.Error( $( '

' ).msg( 'api-error-duplicate-archive', 1 ), { recoverable: false } - ); + ) ); } else if ( warnings[ 'was-deleted' ] !== undefined ) { - return new OO.ui.Error( + return $.Deferred().resolve( new OO.ui.Error( $( '

' ).msg( 'api-error-was-deleted' ), { recoverable: false } - ); + ) ); } else if ( warnings.badfilename !== undefined ) { // Change the name if the current name isn't acceptable // TODO This might not really be the best place to do this this.setFilename( warnings.badfilename ); - return new OO.ui.Error( + return $.Deferred().resolve( new OO.ui.Error( $( '

' ).msg( 'badfilename', warnings.badfilename ) - ); + ) ); } else { - return new OO.ui.Error( + return $.Deferred().resolve( new OO.ui.Error( // Let's get all the help we can if we can't pin point the error $( '

' ).msg( 'api-error-unknown-warning', JSON.stringify( stateDetails ) ), { recoverable: false } - ); + ) ); } } }; -- 2.20.1