wikimedia/mediawiki-core

View on GitHub
resources/src/mediawiki.messagePoster/factory.js

Summary

Maintainability
A
0 mins
Test Coverage
( function () {
    /**
     * @classdesc Factory for MessagePoster objects. This provides a pluggable to way to script the
     * action of adding a message to someone's talk page.
     *
     * The constructor is not publicly accessible; use [mw.messagePoster.factory]{@link mw.messagePoster} instead.
     *
     * @class MessagePosterFactory
     * @singleton
     * @hideconstructor
     */
    function MessagePosterFactory() {
        this.contentModelToClass = Object.create( null );
    }

    OO.initClass( MessagePosterFactory );

    // Note: This registration scheme is currently not compatible with LQT, since that doesn't
    // have its own content model, just islqttalkpage. LQT pages will be passed to the wikitext
    // MessagePoster.
    /**
     * Register a MessagePoster subclass for a given content model.
     *
     * Usage example:
     *
     * ```js
     *   function MyExamplePoster() {}
     *   OO.inheritClass( MyExamplePoster, mw.messagePoster.MessagePoster );
     *
     *   mw.messagePoster.factory.register( 'mycontentmodel', MyExamplePoster );
     * ```
     *
     * The JavaScript files(s) that register message posters for additional content
     * models must be registered with MediaWiki via the `MessagePosterModule`
     * extension attribute, like follows:
     *
     * ```json
     *    "MessagePosterModule": {
     *         "localBasePath": "", // (required)
     *         "scripts": [], // relative file path(s) (required)
     *         "dependencies": [], // module name(s) (optional)
     *    }
     * ```
     *
     * @memberof MessagePosterFactory
     * @param {string} contentModel Content model of pages this MessagePoster can post to
     * @param {Function} constructor Constructor of a MessagePoster subclass
     */
    MessagePosterFactory.prototype.register = function ( contentModel, constructor ) {
        if ( this.contentModelToClass[ contentModel ] ) {
            throw new Error( 'Content model "' + contentModel + '" is already registered' );
        }

        this.contentModelToClass[ contentModel ] = constructor;
    };

    /**
     * Unregister a given content model.
     * This is exposed for testing and should not normally be used.
     *
     * @memberof MessagePosterFactory
     * @param {string} contentModel Content model to unregister
     */
    MessagePosterFactory.prototype.unregister = function ( contentModel ) {
        delete this.contentModelToClass[ contentModel ];
    };

    /**
     * Create a MessagePoster for given a title.
     *
     * A promise for this is returned. It works by determining the content model, then loading
     * the corresponding module (which registers the MessagePoster class), and finally constructing
     * an object for the given title.
     *
     * This does not require the message and should be called as soon as possible, so that the
     * API and ResourceLoader requests run in the background.
     *
     * @memberof MessagePosterFactory
     * @param {mw.Title} title Title that will be posted to
     * @param {string} [apiUrl] api.php URL if the title is on another wiki
     * @return {jQuery.Promise} Promise resolving to a mw.messagePoster.MessagePoster.
     *   For failure, rejected with up to three arguments:
     *
     *   - errorCode Error code string
     *   - error Error explanation
     *   - details Further error details
     */
    MessagePosterFactory.prototype.create = function ( title, apiUrl ) {
        var factory = this,
            api = apiUrl ? new mw.ForeignApi( apiUrl ) : new mw.Api();

        return api.get( {
            formatversion: 2,
            action: 'query',
            prop: 'info',
            titles: title.getPrefixedDb()
        } ).then( function ( data ) {
            var contentModel, page = data.query.pages[ 0 ];
            if ( !page ) {
                return $.Deferred().reject( 'unexpected-response', 'Unexpected API response' );
            }
            contentModel = page.contentmodel;
            if ( !factory.contentModelToClass[ contentModel ] ) {
                return $.Deferred().reject( 'content-model-unknown', 'No handler for "' + contentModel + '"' );
            }
            return new factory.contentModelToClass[ contentModel ]( title, api );
        }, function ( error, details ) {
            return $.Deferred().reject( 'content-model-query-failed', error, details );
        } );
    };

    /**
     * Library for posting messages to talk pages.
     *
     * @namespace mw.messagePoster
     */
    mw.messagePoster = {
        /** @type {MessagePosterFactory} */
        factory: new MessagePosterFactory()
    };
}() );