mw.ForeignStructuredUpload: Provide category suggestions from the right wiki
Also check for category existence on the right wiki, and generate links pointing to the right wiki. Usually. Bug: T116075 Change-Id: I85da301db4cb407b011277b0c00eb09a8bf3829f
This commit is contained in:
parent
7662212384
commit
86dedeea7f
4 changed files with 112 additions and 41 deletions
|
|
@ -2031,6 +2031,8 @@ return array(
|
|||
'dependencies' => array(
|
||||
'oojs-ui',
|
||||
'mediawiki.api',
|
||||
'mediawiki.ForeignApi',
|
||||
'mediawiki.Title',
|
||||
),
|
||||
'targets' => array( 'desktop', 'mobile' ),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -7,42 +7,45 @@
|
|||
( function ( $, mw ) {
|
||||
|
||||
/**
|
||||
* @class mw.widgets.CategoryCapsuleItemWidget
|
||||
* @class mw.widgets.PageExistenceCache
|
||||
* @private
|
||||
* @param {mw.Api} [api]
|
||||
*/
|
||||
|
||||
var processExistenceCheckQueueDebounced,
|
||||
api = new mw.Api(),
|
||||
currentRequest = null,
|
||||
existenceCache = {},
|
||||
existenceCheckQueue = {};
|
||||
|
||||
// The existence checking code really could be refactored into a separate class.
|
||||
function PageExistenceCache( api ) {
|
||||
this.api = api || new mw.Api();
|
||||
this.processExistenceCheckQueueDebounced = OO.ui.debounce( this.processExistenceCheckQueue );
|
||||
this.currentRequest = null;
|
||||
this.existenceCache = {};
|
||||
this.existenceCheckQueue = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for existence of pages in the queue.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function processExistenceCheckQueue() {
|
||||
PageExistenceCache.prototype.processExistenceCheckQueue = function () {
|
||||
var queue, titles;
|
||||
if ( currentRequest ) {
|
||||
if ( this.currentRequest ) {
|
||||
// Don't fire off a million requests at the same time
|
||||
currentRequest.always( function () {
|
||||
currentRequest = null;
|
||||
processExistenceCheckQueueDebounced();
|
||||
} );
|
||||
this.currentRequest.always( function () {
|
||||
this.currentRequest = null;
|
||||
this.processExistenceCheckQueueDebounced();
|
||||
}.bind( this ) );
|
||||
return;
|
||||
}
|
||||
queue = existenceCheckQueue;
|
||||
existenceCheckQueue = {};
|
||||
queue = this.existenceCheckQueue;
|
||||
this.existenceCheckQueue = {};
|
||||
titles = Object.keys( queue ).filter( function ( title ) {
|
||||
if ( existenceCache.hasOwnProperty( title ) ) {
|
||||
queue[ title ].resolve( existenceCache[ title ] );
|
||||
if ( this.existenceCache.hasOwnProperty( title ) ) {
|
||||
queue[ title ].resolve( this.existenceCache[ title ] );
|
||||
}
|
||||
return !existenceCache.hasOwnProperty( title );
|
||||
} );
|
||||
return !this.existenceCache.hasOwnProperty( title );
|
||||
}.bind( this ) );
|
||||
if ( !titles.length ) {
|
||||
return;
|
||||
}
|
||||
currentRequest = api.get( {
|
||||
this.currentRequest = this.api.get( {
|
||||
action: 'query',
|
||||
prop: [ 'info' ],
|
||||
titles: titles
|
||||
|
|
@ -50,14 +53,12 @@
|
|||
var index, curr, title;
|
||||
for ( index in response.query.pages ) {
|
||||
curr = response.query.pages[ index ];
|
||||
title = mw.Title.newFromText( curr.title ).getPrefixedText();
|
||||
existenceCache[ title ] = curr.missing === undefined;
|
||||
queue[ title ].resolve( existenceCache[ title ] );
|
||||
title = new ForeignTitle( curr.title ).getPrefixedText();
|
||||
this.existenceCache[ title ] = curr.missing === undefined;
|
||||
queue[ title ].resolve( this.existenceCache[ title ] );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
processExistenceCheckQueueDebounced = OO.ui.debounce( processExistenceCheckQueue );
|
||||
}.bind( this ) );
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a request to check whether a page exists.
|
||||
|
|
@ -66,16 +67,35 @@
|
|||
* @param {mw.Title} title
|
||||
* @return {jQuery.Promise} Promise resolved with true if the page exists or false otherwise
|
||||
*/
|
||||
function checkPageExistence( title ) {
|
||||
PageExistenceCache.prototype.checkPageExistence = function ( title ) {
|
||||
var key = title.getPrefixedText();
|
||||
if ( !existenceCheckQueue[ key ] ) {
|
||||
existenceCheckQueue[ key ] = $.Deferred();
|
||||
if ( !this.existenceCheckQueue[ key ] ) {
|
||||
this.existenceCheckQueue[ key ] = $.Deferred();
|
||||
}
|
||||
processExistenceCheckQueueDebounced();
|
||||
return existenceCheckQueue[ key ].promise();
|
||||
}
|
||||
this.processExistenceCheckQueueDebounced();
|
||||
return this.existenceCheckQueue[ key ].promise();
|
||||
};
|
||||
|
||||
/**
|
||||
* @class mw.widgets.ForeignTitle
|
||||
* @private
|
||||
* @extends mw.Title
|
||||
*
|
||||
* @constructor
|
||||
* @inheritdoc
|
||||
*/
|
||||
function ForeignTitle() {
|
||||
ForeignTitle.parent.apply( this, arguments );
|
||||
}
|
||||
OO.inheritClass( ForeignTitle, mw.Title );
|
||||
ForeignTitle.prototype.getNamespacePrefix = function () {
|
||||
// We only need to handle categories here...
|
||||
return 'Category:'; // HACK
|
||||
};
|
||||
|
||||
/**
|
||||
* @class mw.widgets.CategoryCapsuleItemWidget
|
||||
*
|
||||
* Category selector capsule item widget. Extends OO.ui.CapsuleItemWidget with the ability to link
|
||||
* to the given page, and to show its existence status (i.e., whether it is a redlink).
|
||||
*
|
||||
|
|
@ -85,6 +105,7 @@
|
|||
* @constructor
|
||||
* @param {Object} config Configuration options
|
||||
* @cfg {mw.Title} title Page title to use (required)
|
||||
* @cfg {string} [apiUrl] API URL, if not the current wiki's API
|
||||
*/
|
||||
mw.widgets.CategoryCapsuleItemWidget = function MWWCategoryCapsuleItemWidget( config ) {
|
||||
// Parent constructor
|
||||
|
|
@ -95,6 +116,7 @@
|
|||
|
||||
// Properties
|
||||
this.title = config.title;
|
||||
this.apiUrl = config.apiUrl || '';
|
||||
this.$link = $( '<a>' )
|
||||
.text( this.label )
|
||||
.attr( 'target', '_blank' )
|
||||
|
|
@ -107,15 +129,39 @@
|
|||
this.setMissing( false );
|
||||
this.$label.replaceWith( this.$link );
|
||||
this.setLabelElement( this.$link );
|
||||
checkPageExistence( this.title ).done( function ( exists ) {
|
||||
this.setMissing( !exists );
|
||||
}.bind( this ) );
|
||||
|
||||
/*jshint -W024*/
|
||||
if ( !this.constructor.static.pageExistenceCaches[ this.apiUrl ] ) {
|
||||
this.constructor.static.pageExistenceCaches[ this.apiUrl ] =
|
||||
new PageExistenceCache( new mw.ForeignApi( this.apiUrl ) );
|
||||
}
|
||||
this.constructor.static.pageExistenceCaches[ this.apiUrl ]
|
||||
.checkPageExistence( new ForeignTitle( this.title.getPrefixedText() ) )
|
||||
.done( function ( exists ) {
|
||||
this.setMissing( !exists );
|
||||
}.bind( this ) );
|
||||
/*jshint +W024*/
|
||||
};
|
||||
|
||||
/* Setup */
|
||||
|
||||
OO.inheritClass( mw.widgets.CategoryCapsuleItemWidget, OO.ui.CapsuleItemWidget );
|
||||
|
||||
/* Static Properties */
|
||||
|
||||
/*jshint -W024*/
|
||||
/**
|
||||
* Map of API URLs to PageExistenceCache objects.
|
||||
*
|
||||
* @static
|
||||
* @inheritable
|
||||
* @property {Object}
|
||||
*/
|
||||
mw.widgets.CategoryCapsuleItemWidget.static.pageExistenceCaches = {
|
||||
'': new PageExistenceCache()
|
||||
};
|
||||
/*jshint +W024*/
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
|
|
@ -125,13 +171,17 @@
|
|||
* @param {boolean} missing Whether the page is missing (does not exist)
|
||||
*/
|
||||
mw.widgets.CategoryCapsuleItemWidget.prototype.setMissing = function ( missing ) {
|
||||
var
|
||||
title = new ForeignTitle( this.title.getPrefixedText() ), // HACK
|
||||
prefix = this.apiUrl.replace( '/w/api.php', '' ); // HACK
|
||||
|
||||
if ( !missing ) {
|
||||
this.$link
|
||||
.attr( 'href', this.title.getUrl() )
|
||||
.attr( 'href', prefix + title.getUrl() )
|
||||
.removeClass( 'new' );
|
||||
} else {
|
||||
this.$link
|
||||
.attr( 'href', this.title.getUrl( { action: 'edit', redlink: 1 } ) )
|
||||
.attr( 'href', prefix + title.getUrl( { action: 'edit', redlink: 1 } ) )
|
||||
.addClass( 'new' );
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
*
|
||||
* @constructor
|
||||
* @param {Object} [config] Configuration options
|
||||
* @cfg {mw.Api} [api] Instance of mw.Api (or subclass thereof) to use for queries
|
||||
* @cfg {number} [limit=10] Maximum number of results to load
|
||||
* @cfg {mw.widgets.CategorySelector.SearchType[]} [searchTypes=[mw.widgets.CategorySelector.SearchType.OpenSearch]]
|
||||
* Default search API to use when searching.
|
||||
|
|
@ -61,7 +62,7 @@
|
|||
this.$input.on( 'change input cut paste', OO.ui.debounce( this.updateMenuItems.bind( this ), 100 ) );
|
||||
|
||||
// Initialize
|
||||
this.api = new mw.Api();
|
||||
this.api = config.api || new mw.Api();
|
||||
}
|
||||
|
||||
/* Setup */
|
||||
|
|
@ -178,6 +179,7 @@
|
|||
*/
|
||||
CSP.createItemWidget = function ( data ) {
|
||||
return new mw.widgets.CategoryCapsuleItemWidget( {
|
||||
apiUrl: this.api.apiUrl || undefined,
|
||||
title: mw.Title.newFromText( data, NS_CATEGORY )
|
||||
} );
|
||||
};
|
||||
|
|
|
|||
|
|
@ -35,6 +35,21 @@
|
|||
|
||||
/* Uploading */
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
mw.ForeignStructuredUpload.BookletLayout.prototype.initialize = function () {
|
||||
mw.ForeignStructuredUpload.BookletLayout.parent.prototype.initialize.call( this );
|
||||
// Point the CategorySelector to the right wiki as soon as we know what the right wiki is
|
||||
this.upload.apiPromise.done( function ( api ) {
|
||||
// If this is a ForeignApi, it will have a apiUrl, otherwise we don't need to do anything
|
||||
if ( api.apiUrl ) {
|
||||
// Can't reuse the same object, CategorySelector calls #abort on its mw.Api instance
|
||||
this.categoriesWidget.api = new mw.ForeignApi( api.apiUrl );
|
||||
}
|
||||
}.bind( this ) );
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a {@link mw.ForeignStructuredUpload mw.ForeignStructuredUpload}
|
||||
* with the {@link #cfg-target target} specified in config.
|
||||
|
|
@ -150,6 +165,8 @@
|
|||
mustBeBefore: moment().add( 1, 'day' ).locale( 'en' ).format( 'YYYY-MM-DD' ) // Tomorrow
|
||||
} );
|
||||
this.categoriesWidget = new mw.widgets.CategorySelector( {
|
||||
// Can't be done here because we don't know the target wiki yet... done in #initialize.
|
||||
// api: new mw.ForeignApi( ... ),
|
||||
$overlay: this.$overlay
|
||||
} );
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue