wikimedia/mediawiki-extensions-UploadWizard

View on GitHub
resources/mw.DestinationChecker.js

Summary

Maintainability
B
5 hrs
Test Coverage
( function () {

    /**
     * @class
     */
    mw.DestinationChecker = {

        api: new mw.Api(),

        // cached results from uniqueness api calls
        cachedResult: {},
        cachedBlacklist: {},

        /**
         * Check title for validity.
         *
         * @param {string} title Title to check
         * @return {jQuery.Promise}
         * @return {Function} return.done
         * @return {string} return.done.title The title that was passed in
         * @return {Object|boolean} return.done.blacklist See #checkBlacklist
         * @return {Object|boolean} return.done.unique See #checkUnique
         */
        checkTitle: function ( title ) {
            return $.when(
                this.checkUnique( title ),
                this.checkBlacklist( title )
            ).then( ( unique, blacklist ) => ( {
                unique: unique,
                blacklist: blacklist,
                title: title
            } ) );
        },

        /**
         * Async check if a title is in the titleblacklist.
         *
         * @param {string} title Title to check against the blacklist
         * @return {jQuery.Promise}
         * @return {Function} return.done
         * @return {boolean} return.done.notBlacklisted
         * @return {string} [return.done.blacklistReason] See mw.Api#isBlacklisted
         * @return {string} [return.done.blacklistMessage] See mw.Api#isBlacklisted
         * @return {string} [return.done.blacklistLine] See mw.Api#isBlacklisted
         */
        checkBlacklist: function ( title ) {
            var checker = this;

            /**
             * Process result of a TitleBlacklist API call.
             *
             * @private
             * @param {Object|boolean} blacklistResult `false` if not blacklisted, object if blacklisted
             * @return {Object}
             */
            function blacklistResultProcessor( blacklistResult ) {
                var result;

                if ( blacklistResult === false ) {
                    result = { notBlacklisted: true };
                } else {
                    result = {
                        notBlacklisted: false,
                        blacklistReason: blacklistResult.reason,
                        blacklistMessage: blacklistResult.message,
                        blacklistLine: blacklistResult.line
                    };
                }

                checker.cachedBlacklist[ title ] = result;
                return result;
            }

            if ( this.cachedBlacklist[ title ] !== undefined ) {
                return $.Deferred().resolve( this.cachedBlacklist[ title ] );
            }

            // it's not blacklisted, because the API isn't even available
            return mw.loader.using( 'mediawiki.api.titleblacklist' ).then( () => checker.api.isBlacklisted( title ).then( blacklistResultProcessor ), () => $.Deferred().resolve( { notBlacklisted: true, unavailable: true } ) );
        },

        /**
         * Async check if a filename is unique. Can be attached to a field's change() event
         * This is a more abstract version of AddMedia/UploadHandler.js::doDestCheck
         *
         * @param {string} title Title to check for uniqueness
         * @return {jQuery.Promise}
         * @return {Function} return.done
         * @return {boolean} return.done.isUnique
         * @return {boolean} [return.done.isProtected]
         * @return {Object} [return.done.img] Image info
         * @return {string} [return.done.href] URL to file description page
         */
        checkUnique: function ( title ) {
            var checker = this,
                NS_FILE = mw.config.get( 'wgNamespaceIds' ).file,
                titleObj, prefix, ext;

            titleObj = mw.Title.newFromText( title );
            ext = mw.Title.normalizeExtension( titleObj.getExtension() || '' );
            // Strip namespace and file extension
            prefix = titleObj.getNameText();

            /**
             * Process result of a an imageinfo API call.
             *
             * @private
             * @param {Object} data API result
             * @return {Object}
             */
            function checkUniqueProcessor( data ) {
                var result, protection, pageId, ntitle, ntitleObj, img;

                result = { isUnique: true };

                if ( data.query && data.query.pages ) {
                    // The API will check for files with that filename.
                    // If no file found: a page with a key of -1 and no imageinfo
                    // If file found on another repository, such as when the wiki is using InstantCommons: page with a key of -1, plus imageinfo
                    // If file found on this repository: page with some positive numeric key
                    if ( data.query.pages[ -1 ] && !data.query.pages[ -1 ].imageinfo ) {
                        protection = data.query.pages[ -1 ].protection;
                        if ( protection && protection.length > 0 ) {
                            protection.forEach( ( val ) => {
                                if ( mw.config.get( 'wgUserGroups' ).indexOf( val.level ) === -1 ) {
                                    result = {
                                        isUnique: true,
                                        isProtected: true
                                    };
                                }
                            } );
                        } else {
                            // No conflict found on any repository this wiki uses
                            result = { isUnique: true };
                        }
                    } else {
                        for ( pageId in data.query.pages ) {
                            if ( !Object.prototype.hasOwnProperty.call( data.query.pages, pageId ) ) {
                                continue;
                            }
                            ntitle = data.query.pages[ pageId ].title;
                            ntitleObj = mw.Title.newFromText( ntitle );
                            if ( ntitleObj.getNameText() !== prefix ) {
                                // It's a different file name entirely
                                continue;
                            }
                            if ( ext !== mw.Title.normalizeExtension( ntitleObj.getExtension() || '' ) ) {
                                // It's a different extension, that's fine (e.g. to upload a SVG version of a PNG file)
                                continue;
                            }

                            // Conflict found, this filename is NOT unique

                            if ( !data.query.pages[ pageId ].imageinfo ) {
                                // This means that there's a page, but it's not a file. Well,
                                // we should really report that anyway, but we shouldn't process
                                // it like a file, and we should defer to other entries that may be files.
                                result = {
                                    isUnique: false,
                                    title: ntitle,
                                    img: null,
                                    href: null
                                };
                                continue;
                            }

                            img = data.query.pages[ pageId ].imageinfo[ 0 ];

                            result = {
                                isUnique: false,
                                img: img,
                                title: ntitle,
                                href: img.descriptionurl
                            };

                            break;
                        }
                    }
                }

                return result;
            }

            if ( this.cachedResult[ title ] !== undefined ) {
                return $.Deferred().resolve( this.cachedResult[ title ] );
            }

            // Setup the request -- will return thumbnail data if it finds one
            // XXX do not use iiurlwidth as it will create a thumbnail
            return $.when(
                // Checks for exact matches on this wiki and foreign file repos
                this.api.get( {
                    action: 'query',
                    titles: title,
                    prop: 'info|imageinfo',
                    inprop: 'protection',
                    iiprop: 'url|mime|size',
                    iiurlwidth: 150
                } ).then( checkUniqueProcessor ),
                // Checks for matches with different versions of the file extension on this wiki only
                this.api.get( {
                    action: 'query',
                    generator: 'allpages',
                    gapnamespace: NS_FILE,
                    gapprefix: prefix,
                    prop: 'info|imageinfo',
                    inprop: 'protection',
                    iiprop: 'url|mime|size',
                    iiurlwidth: 150
                } ).then( checkUniqueProcessor )
            ).then( ( exact, fuzzy ) => {
                var result;
                if ( !exact.isUnique || exact.isProtected ) {
                    result = exact;
                } else if ( !fuzzy.isUnique || fuzzy.isProtected ) {
                    result = fuzzy;
                } else {
                    result = { isUnique: true };
                }

                checker.cachedResult[ title ] = result;
                return result;
            } );
        },

        /**
         * Clears the result cache
         */
        clearCache: function () {
            this.cachedResult = {};
        }

    };

}() );