Use parsed HTML error responses instead of api-error-*
authorMatthias Mullie <git@mullie.eu>
Sun, 8 Jan 2017 19:10:34 +0000 (20:10 +0100)
committerMatthias Mullie <git@mullie.eu>
Tue, 17 Jan 2017 17:53:05 +0000 (09:53 -0800)
There is no point in keeping these api-error- messages.
They were originally introduced in UploadWizard, then moved
to core, where nothing else (apart from some in BookletLayout)
uses them. They are mostly duplicates of other messages in
core, and these are very easy to miss when updating a message
in core.

Instead of building the messages on the frontend, we're much
better off requesting a parsed error response from the API
right away. This also lets us get rid of sime hacks (e.g. for
AbuseFilter)

And then a few other api-error-* messages have been removed
in favor of their alternatives (which are also used elsewhere)

And stashfailed is not a warning. Maybe it was at some point,
but stashfailed now dies with an error.

Bug: T47843
Change-Id: I41d88e2aad308a1fecd10d97085345a17d0c3603

languages/i18n/en.json
languages/i18n/qqq.json
resources/Resources.php
resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js
resources/src/mediawiki/mediawiki.Upload.BookletLayout.js

index 1e37d40..0d9b1ee 100644 (file)
        "feedback-useragent": "User agent:",
        "searchsuggest-search": "Search {{SITENAME}}",
        "searchsuggest-containing": "containing...",
-       "api-error-autoblocked": "Your IP address has been blocked automatically, because it was used by a blocked user.",
-       "api-error-badaccess-groups": "You are not permitted to upload files to this wiki.",
        "api-error-badtoken": "Internal error: Bad token.",
-       "api-error-blocked": "You have been blocked from editing.",
-       "api-error-copyuploaddisabled": "Uploading by URL is disabled on this server.",
-       "api-error-duplicate": "There {{PLURAL:$1|is another file|are some other files}} already on the site with the same content.",
-       "api-error-duplicate-archive": "There {{PLURAL:$1|was another file|were some other files}} already on the site with the same content, but {{PLURAL:$1|it was|they were}} deleted.",
-       "api-error-empty-file": "The file you submitted was empty.",
        "api-error-emptypage": "Creating new, empty pages is not allowed.",
-       "api-error-fetchfileerror": "Internal error: Something went wrong while fetching the file.",
-       "api-error-fileexists-forbidden": "A file with name \"$1\" already exists, and cannot be overwritten.",
-       "api-error-fileexists-shared-forbidden": "A file with name \"$1\" already exists in the shared file repository, and cannot be overwritten.",
-       "api-error-file-too-large": "The file you submitted was too large.",
-       "api-error-filename-tooshort": "The filename is too short.",
-       "api-error-filetype-banned": "This type of file is banned.",
-       "api-error-filetype-banned-type": "$1 {{PLURAL:$4|is not a permitted file type|are not permitted file types}}. Permitted {{PLURAL:$3|file type is|file types are}} $2.",
-       "api-error-filetype-missing": "The filename is missing an extension.",
-       "api-error-hookaborted": "The modification you tried to make was aborted by an extension.",
-       "api-error-http": "Internal error: Unable to connect to server.",
-       "api-error-illegal-filename": "The filename is not allowed.",
-       "api-error-internal-error": "Internal error: Something went wrong with processing your upload on the wiki.",
-       "api-error-invalid-file-key": "Internal error: File was not found in temporary storage.",
-       "api-error-missingparam": "Internal error: Missing parameters on request.",
-       "api-error-missingresult": "Internal error: Could not determine if the copy succeeded.",
-       "api-error-mustbeloggedin": "You must be logged in to upload files.",
-       "api-error-mustbeposted": "Internal error: Request requires HTTP POST.",
-       "api-error-noimageinfo": "The upload succeeded, but the server did not give us any information about the file.",
-       "api-error-nomodule": "Internal error: No upload module set.",
-       "api-error-ok-but-empty": "Internal error: No response from server.",
-       "api-error-overwrite": "Overwriting an existing file is not allowed.",
-       "api-error-ratelimited": "You're trying to upload more files in a short space of time than this wiki allows.\nPlease try again in a few minutes.",
-       "api-error-stashfailed": "Internal error: Server failed to store temporary file.",
        "api-error-publishfailed": "Internal error: Server failed to publish temporary file.",
-       "api-error-stasherror": "There was an error while uploading the file to stash.",
-       "api-error-stashedfilenotfound": "The stashed file was not found when attempting to upload it from the stash.",
-       "api-error-stashpathinvalid": "The path at which the stashed file should have been found was invalid.",
-       "api-error-stashfilestorage": "There was an error while storing the file in the stash.",
-       "api-error-stashzerolength": "The server could not stash the file, because it had zero length.",
-       "api-error-stashnotloggedin": "You must be logged in to save files in the upload stash.",
-       "api-error-stashwrongowner": "The file you were attempting to access in the stash does not belong to you.",
-       "api-error-stashnosuchfilekey": "The file key you were attempting to access in the stash does not exist.",
-       "api-error-timeout": "The server did not respond within the expected time.",
-       "api-error-unclassified": "An unknown error occurred.",
-       "api-error-unknown-code": "Unknown error: \"$1\".",
-       "api-error-unknown-error": "Internal error: Something went wrong when trying to upload your file.",
+       "api-error-stashfailed": "Internal error: Server failed to store temporary file.",
        "api-error-unknown-warning": "Unknown warning: \"$1\".",
        "api-error-unknownerror": "Unknown error: \"$1\".",
-       "api-error-uploaddisabled": "Uploading is disabled on this wiki.",
-       "api-error-verification-error": "This file might be corrupt, or have the wrong extension.",
-       "api-error-was-deleted": "A file of this name has been previously uploaded and subsequently deleted.",
        "duration-seconds": "$1 {{PLURAL:$1|second|seconds}}",
        "duration-minutes": "$1 {{PLURAL:$1|minute|minutes}}",
        "duration-hours": "$1 {{PLURAL:$1|hour|hours}}",
index 2f16979..42c4ae5 100644 (file)
        "feedback-useragent": "A label denoting the user agent in the feedback that is posted to the feedback page.\n{{Identical|User agent}}",
        "searchsuggest-search": "Greyed out default text in the simple search box in the Vector skin. (It disappears and lets the user enter the requested search terms when the search box receives focus.)\n{{Identical|Search}}",
        "searchsuggest-containing": "Label used in the special item of the search suggestions list which gives the user an option to perform a full text search for the term.",
-       "api-error-autoblocked": "API error message that can be used for client side localisation of API errors.\n\nCf. {{msg-mw|Autoblockedtext}}.",
-       "api-error-badaccess-groups": "API error message that can be used for client side localisation of API errors.",
        "api-error-badtoken": "API error message that can be used for client side localisation of API errors.",
-       "api-error-blocked": "API error message that can be used for client side localisation of API errors.",
-       "api-error-copyuploaddisabled": "API error message that can be used for client side localisation of API errors.",
-       "api-error-duplicate": "API error message that can be used for client side localisation of API errors. Parameters:\n* $1 - a number of files",
-       "api-error-duplicate-archive": "API error message that can be used for client side localisation of API errors. Parameters:\n* $1 - a number of files",
-       "api-error-empty-file": "API error message that can be used for client side localisation of API errors.",
        "api-error-emptypage": "API error message that can be used for client side localisation of API errors.",
-       "api-error-fetchfileerror": "API error message that can be used for client side localisation of API errors.",
-       "api-error-fileexists-forbidden": "API error message that can be used for client side localisation of API errors.\n\nParameters:\n* $1 - filename\nSee also:\n* {{msg-mw|Api-error-fileexists-shared-forbidden}}",
-       "api-error-fileexists-shared-forbidden": "API error message that can be used for client side localisation of API errors.\n\nParameters:\n* $1 - filename\nSee also:\n* {{msg-mw|Api-error-fileexists-forbidden}}",
-       "api-error-file-too-large": "API error message that can be used for client side localisation of API errors.",
-       "api-error-filename-tooshort": "API error message that can be used for client side localisation of API errors.",
-       "api-error-filetype-banned": "API error message that can be used for client side localisation of API errors.",
-       "api-error-filetype-banned-type": "API error message that can be used for client side localisation of API errors.\n\n* $1 is the extension(s) of the file which cannot be uploaded\n* $2 is the list of file extensions that can be uploaded (Example: ''png, gif, jpg, jpeg, ogg, pdf, svg.'')\n* $3 is the number of allowed file formats (to be used for the PLURAL function)\n* $4 is the number of extensions that could not be uploaded (to be used for the PLURAL function)",
-       "api-error-filetype-missing": "The word \"extension\" refers to the part behind the last dot in a file name, that by convention gives a hint about the kind of data format which a files contents are in.",
-       "api-error-hookaborted": "The word \"extension\" here refers to a [[:mw:Manual:Extensions|MediaWiki Extension]] which extends the functionality of the basic wiki by adding something to its capabilities. \"… aborted by an extension\" implies that an operation could not be performed successfully or was not allowed to continue to its intended end.",
-       "api-error-http": "API error message that can be used for client side localisation of API errors.",
-       "api-error-illegal-filename": "API error message that can be used for client side localisation of API errors.",
-       "api-error-internal-error": "API error message that can be used for client side localisation of API errors.",
-       "api-error-invalid-file-key": "API error message that can be used for client side localisation of API errors.",
-       "api-error-missingparam": "API error message that can be used for client side localisation of API errors.",
-       "api-error-missingresult": "API error message that can be used for client side localisation of API errors.",
-       "api-error-mustbeloggedin": "API error message that can be used for client side localisation of API errors.",
-       "api-error-mustbeposted": "API error message that can be used for client side localisation of API errors.",
-       "api-error-noimageinfo": "API error message that can be used for client side localisation of API errors.",
-       "api-error-nomodule": "API error message that can be used for client side localisation of API errors.",
-       "api-error-ok-but-empty": "API error message that can be used for client side localisation of API errors.",
-       "api-error-overwrite": "API error message that can be used for client side localisation of API errors.",
-       "api-error-ratelimited": "API error message that can be used for client side localisation of API errors.\n\nCf. {{msg-mw|Actionthrottledtext}}",
        "api-error-stashfailed": "API error message that can be used for client side localisation of API errors.",
        "api-error-publishfailed": "API error message that can be used for client side localisation of API errors.",
-       "api-error-stasherror": "API error message that can be used for client side localisation of API errors.",
-       "api-error-stashedfilenotfound": "API error message that can be used for client side localisation of API errors.",
-       "api-error-stashpathinvalid": "API error message that can be used for client side localisation of API errors.",
-       "api-error-stashfilestorage": "API error message that can be used for client side localisation of API errors.",
-       "api-error-stashzerolength": "API error message that can be used for client side localisation of API errors.",
-       "api-error-stashnotloggedin": "API error message that can be used for client side localisation of API errors.",
-       "api-error-stashwrongowner": "API error message that can be used for client side localisation of API errors.",
-       "api-error-stashnosuchfilekey": "API error message that can be used for client side localisation of API errors.",
-       "api-error-timeout": "API error message that can be used for client side localisation of API errors.",
-       "api-error-unclassified": "API error message that can be used for client side localisation of API errors.",
-       "api-error-unknown-code": "API error message that can be used for client side localisation of API errors.\n\nParameters:\n* $1 - may contain more error details\n{{Identical|Unknown error}}",
-       "api-error-unknown-error": "API error message that can be used for client side localisation of API errors.",
        "api-error-unknown-warning": "API error message that can be used for client side localisation of API errors. Parameters:\n* $1 is an unknown warning.",
        "api-error-unknownerror": "API error message that can be used for client side localisation of API errors.\n\nParameters:\n* $1 - an unknown error message\n{{Identical|Unknown error}}",
-       "api-error-uploaddisabled": "API error message that can be used for client side localisation of API errors.",
-       "api-error-verification-error": "The word \"extension\" refers to the part behind the last dot in a file name, that by convention gives a hint about the kind of data format which a files contents are in.",
-       "api-error-was-deleted": "API error message that can be used for client side localisation of API errors.",
        "duration-seconds": "Used as duration. Parameters:\n* $1 - number of seconds\n{{Related|Duration}}\n{{Identical|Second}}",
        "duration-minutes": "Used as duration. Parameters:\n* $1 - number of minutes\n{{Related|Duration}}\n{{Identical|Minute}}",
        "duration-hours": "Used as duration. Parameters:\n* $1 - number of hours\n{{Related|Duration}}\n{{Identical|Hour}}",
index 7c00feb..7006a06 100644 (file)
@@ -1276,59 +1276,18 @@ return [
                        'upload-form-label-infoform-description-tooltip',
                        'upload-form-label-usage-title',
                        'upload-form-label-usage-filename',
-                       'api-error-unknownerror',
+                       'action-upload',
+                       'apierror-mustbeloggedin',
+                       'badaccess-groups',
+                       'apierror-unknownerror',
                        'api-error-unknown-warning',
-                       'api-error-autoblocked',
-                       'api-error-blocked',
-                       'api-error-badaccess-groups',
-                       'api-error-badtoken',
-                       'api-error-copyuploaddisabled',
-                       'api-error-duplicate',
-                       'api-error-duplicate-archive',
-                       'api-error-empty-file',
-                       'api-error-emptypage',
-                       'api-error-fetchfileerror',
-                       'api-error-fileexists-forbidden',
-                       'api-error-fileexists-shared-forbidden',
-                       'api-error-file-too-large',
-                       'api-error-filename-tooshort',
-                       'api-error-filetype-banned',
-                       'api-error-filetype-banned-type',
-                       'api-error-filetype-missing',
-                       'api-error-hookaborted',
-                       'api-error-http',
-                       'api-error-illegal-filename',
-                       'api-error-internal-error',
-                       'api-error-invalid-file-key',
-                       'api-error-missingparam',
-                       'api-error-missingresult',
-                       'api-error-mustbeloggedin',
-                       'api-error-mustbeposted',
-                       'api-error-noimageinfo',
-                       'api-error-nomodule',
-                       'api-error-ok-but-empty',
-                       'api-error-overwrite',
-                       'api-error-stashfailed',
-                       'api-error-publishfailed',
-                       'api-error-stasherror',
-                       'api-error-stashedfilenotfound',
-                       'api-error-stashpathinvalid',
-                       'api-error-stashfilestorage',
-                       'api-error-stashzerolength',
-                       'api-error-stashnotloggedin',
-                       'api-error-stashwrongowner',
-                       'api-error-stashnosuchfilekey',
-                       'api-error-timeout',
-                       'api-error-unclassified',
-                       'api-error-unknown-code',
-                       'api-error-unknown-error',
-                       'api-error-uploaddisabled',
-                       'api-error-verification-error',
-                       'api-error-was-deleted',
                        'fileexists',
                        'filepageexists',
+                       'file-exists-duplicate',
+                       'file-deleted-duplicate',
                        'filename-bad-prefix',
                        'filename-thumb-name',
+                       'filewasdeleted',
                        'badfilename',
                        'protectedpagetext',
                ],
index 543ece8..f2b6f5f 100644 (file)
         * @return {mw.Upload}
         */
        mw.ForeignStructuredUpload.BookletLayout.prototype.createUpload = function () {
-               return new mw.ForeignStructuredUpload( this.target );
+               return new mw.ForeignStructuredUpload( this.target, {
+                       parameters: {
+                               errorformat: 'html',
+                               errorlang: mw.config.get( 'wgUserLanguage' ),
+                               errorsuselocal: 1,
+                               formatversion: 2
+                       }
+               } );
        };
 
        /* Form renderers */
index f736036..172cac2 100644 (file)
                                        function ( userInfo ) {
                                                if ( userInfo.rights.indexOf( 'upload' ) === -1 ) {
                                                        if ( mw.user.isAnon() ) {
-                                                               booklet.getPage( 'upload' ).$element.msg( 'api-error-mustbeloggedin' );
+                                                               booklet.getPage( 'upload' ).$element.msg( 'apierror-mustbeloggedin', mw.msg( 'action-upload' ) );
                                                        } else {
-                                                               booklet.getPage( 'upload' ).$element.msg( 'api-error-badaccess-groups' );
+                                                               booklet.getPage( 'upload' ).$element.msg( 'apierror-permissiondenied', mw.msg( 'action-upload' ) );
                                                        }
                                                }
                                                return $.Deferred().resolve();
         * @return {mw.Upload} Upload model
         */
        mw.Upload.BookletLayout.prototype.createUpload = function () {
-               return new mw.Upload();
+               return new mw.Upload( {
+                       parameters: {
+                               errorformat: 'html',
+                               errorlang: mw.config.get( 'wgUserLanguage' ),
+                               errorsuselocal: 1,
+                               formatversion: 2
+                       }
+               } );
        };
 
        /* Uploading */
         * @return {jQuery.Promise} A Promise that will be resolved with an OO.ui.Error.
         */
        mw.Upload.BookletLayout.prototype.getErrorMessageForStateDetails = function () {
-               var message,
-                       state = this.upload.getState(),
+               var state = this.upload.getState(),
                        stateDetails = this.upload.getStateDetails(),
-                       error = stateDetails.error,
-                       warnings = stateDetails.upload && stateDetails.upload.warnings;
+                       error = stateDetails.errors ? stateDetails.errors[ 0 ] : false,
+                       warnings = stateDetails.upload && stateDetails.upload.warnings,
+                       $ul = $( '<ul>' );
 
                if ( state === mw.Upload.State.ERROR ) {
                        if ( !error ) {
                                // If there's an 'exception' key, this might be a timeout, or other connection problem
                                return $.Deferred().resolve( new OO.ui.Error(
-                                       $( '<p>' ).msg( 'api-error-unknownerror', JSON.stringify( stateDetails ) ),
+                                       $( '<p>' ).msg( 'apierror-unknownerror', JSON.stringify( stateDetails ) ),
                                        { 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 ) {
-                                               // 'amenableparser' will expand templates and parser functions server-side.
-                                               // We still do the rest of wikitext parsing here (through jqueryMsg).
-                                               return api.loadMessagesIfMissing( [ error.message.key ], { amenableparser: true } )
-                                                       .then( function () {
-                                                               if ( !mw.message( error.message.key ).exists() ) {
-                                                                       return $.Deferred().reject();
-                                                               }
-                                                               return new OO.ui.Error(
-                                                                       $( '<p>' ).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(
-                                                       $( '<p>' ).msg( 'api-error-unknownerror', JSON.stringify( stateDetails ) ),
-                                                       { recoverable: false }
-                                               ) );
-                                       } );
-                       }
-
-                       if ( error.code === 'protectedpage' ) {
-                               message = mw.message( 'protectedpagetext' );
-                       } else {
-                               message = mw.message( 'api-error-' + error.code );
-                               if ( !message.exists() ) {
-                                       message = mw.message( 'api-error-unknownerror', JSON.stringify( stateDetails ) );
-                               }
-                       }
                        return $.Deferred().resolve( new OO.ui.Error(
-                               $( '<p>' ).append( message.parseDom() ),
+                               $( '<p>' ).html( error.html ),
                                { recoverable: false }
                        ) );
                }
                        // We could get more than one of these errors, these are in order
                        // 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 $.Deferred().resolve( new OO.ui.Error(
-                                       $( '<p>' ).msg( 'api-error-stashfailed' ),
-                                       { recoverable: false }
-                               ) );
-                       } else if ( warnings.exists !== undefined ) {
+                       if ( warnings.exists !== undefined ) {
                                return $.Deferred().resolve( new OO.ui.Error(
                                        $( '<p>' ).msg( 'fileexists', 'File:' + warnings.exists ),
                                        { recoverable: false }
                                        { recoverable: false }
                                ) );
                        } else if ( warnings.duplicate !== undefined ) {
+                               $.each( warnings.duplicate, function ( i, filename ) {
+                                       var $a = $( '<a>' ).text( filename ),
+                                               href = mw.Title.makeTitle( mw.config.get( 'wgNamespaceIds' ).file, filename ).getUrl( {} );
+
+                                       $a.attr( { href: href, target: '_blank' } );
+                                       $ul.append( $( '<li>' ).append( $a ) );
+                               } );
+
                                return $.Deferred().resolve( new OO.ui.Error(
-                                       $( '<p>' ).msg( 'api-error-duplicate', warnings.duplicate.length ),
+                                       $( '<p>' ).msg( 'file-exists-duplicate', warnings.duplicate.length ).append( $ul ),
                                        { recoverable: false }
                                ) );
                        } else if ( warnings[ 'thumb-name' ] !== undefined ) {
                                ) );
                        } else if ( warnings[ 'duplicate-archive' ] !== undefined ) {
                                return $.Deferred().resolve( new OO.ui.Error(
-                                       $( '<p>' ).msg( 'api-error-duplicate-archive', 1 ),
+                                       $( '<p>' ).msg( 'file-deleted-duplicate', 'File:' + warnings[ 'duplicate-archive' ] ),
                                        { recoverable: false }
                                ) );
                        } else if ( warnings[ 'was-deleted' ] !== undefined ) {
                                return $.Deferred().resolve( new OO.ui.Error(
-                                       $( '<p>' ).msg( 'api-error-was-deleted' ),
+                                       $( '<p>' ).msg( 'filewasdeleted', 'File:' + warnings[ 'was-deleted' ] ),
                                        { recoverable: false }
                                ) );
                        } else if ( warnings.badfilename !== undefined ) {