mediawiki-extensions-Discus.../modules/dt-ve/dt.ui.UsernameCompletionAction.js
Ed Sanders 998bd206ae Follow-up I60e550ef: Don't show relevant user in mention list if it is you
We have logic above to exclude mentioning yourself, but this
is overridden if you are commenting on your own talk page.

Change-Id: I2858c79bd9f1cb733f105825e17f9df75859e40a
2021-03-04 20:59:00 +00:00

181 lines
6.1 KiB
JavaScript

/*!
* VisualEditor UserInterface MWUsernameCompletionAction class.
*
* @copyright 2011-2019 VisualEditor Team and others; see http://ve.mit-license.org
*/
var openCommand, insertAndOpenCommand, sequence,
controller = require( 'ext.discussionTools.init' ).controller;
/**
* MWUsernameCompletionAction action.
*
* Controls autocompletion of usernames
*
* @class
* @extends ve.ui.CompletionAction
* @constructor
* @param {ve.ui.Surface} surface Surface to act on
*/
function MWUsernameCompletionAction( surface ) {
var action = this,
relevantUserName = mw.config.get( 'wgRelevantUserName' );
// Parent constructor
MWUsernameCompletionAction.super.call( this, surface );
// Shared API object so previous requests can be aborted
this.api = controller.getApi();
this.searchedPrefixes = {};
this.localUsers = [];
this.ipUsers = [];
this.surface.authors.forEach( function ( user ) {
if ( mw.util.isIPAddress( user ) ) {
action.ipUsers.push( user );
} else if ( user !== mw.user.getName() ) {
action.localUsers.push( user );
}
} );
if (
relevantUserName &&
relevantUserName !== mw.user.getName() &&
this.localUsers.indexOf( relevantUserName ) === -1
) {
this.localUsers.push( relevantUserName );
this.localUsers.sort();
}
this.remoteUsers = [];
}
/* Inheritance */
OO.inheritClass( MWUsernameCompletionAction, ve.ui.CompletionAction );
/* Static Properties */
MWUsernameCompletionAction.static.name = 'mwUsernameCompletion';
MWUsernameCompletionAction.static.methods = OO.copy( MWUsernameCompletionAction.static.methods );
MWUsernameCompletionAction.static.methods.push( 'insertAndOpen' );
/* Methods */
MWUsernameCompletionAction.prototype.insertAndOpen = function () {
// This is opening a window in a slightly weird way, so the normal logging
// doesn't catch it. This assumes that the only way to get here is from
// the tool. If we add other paths, we'd need to change the logging.
ve.track(
'activity.' + this.constructor.static.name,
{ action: 'window-open-from-tool' }
);
this.surface.getModel().getFragment().insertContent( '@' ).collapseToEnd().select();
return this.open();
};
MWUsernameCompletionAction.prototype.getSuggestions = function ( input ) {
var apiPromise,
capitalizedInput = input.length > 0 && input[ 0 ].toUpperCase() + input.slice( 1 ),
title = mw.Title.makeTitle( mw.config.get( 'wgNamespaceIds' ).user, input ),
validatedInput = title ? input : '',
action = this;
this.api.abort(); // Abort all unfinished API requests
if ( capitalizedInput && !this.searchedPrefixes[ capitalizedInput ] ) {
apiPromise = this.api.get( {
action: 'query',
list: 'allusers',
// Prefix of list=allusers is case sensitive, and users are stored in the DB capitalized, so:
auprefix: capitalizedInput,
aulimit: this.limit
} ).then( function ( response ) {
var suggestions = response.query.allusers.map( function ( user ) {
return user.name;
} ).filter( function ( username ) {
// API doesn't return IPs
return action.localUsers.indexOf( username ) === -1 &&
action.remoteUsers.indexOf( username ) === -1;
} );
action.remoteUsers.push.apply( action.remoteUsers, suggestions );
action.remoteUsers.sort();
action.searchedPrefixes[ capitalizedInput ] = true;
} );
} else {
apiPromise = ve.createDeferred().resolve().promise();
}
return apiPromise.then( function () {
// By concatenating on-thread authors and remote-fetched authors, both
// sorted alphabetically, we'll get our suggestion popup sorted so all
// on-thread matches come first.
return action.filterSuggestionsForInput(
action.localUsers
// Show no remote users if no input provided
.concat( capitalizedInput ? action.remoteUsers : [] ),
// TODO: Consider showing IP users
// * Change link to Special:Contributions/<ip> (localised)
// * Let users know that mentioning an IP will not create a notification?
// .concat( this.ipUsers )
validatedInput
);
} );
};
MWUsernameCompletionAction.prototype.getHeaderLabel = function ( input, suggestions ) {
var $query;
if ( suggestions === undefined ) {
$query = $( '<span>' ).text( input );
return mw.message( 'discussiontools-replywidget-mention-tool-header', $query ).parseDom();
}
};
MWUsernameCompletionAction.prototype.insertCompletion = function ( word, range ) {
var fragment,
prefix = mw.msg( 'discussiontools-replywidget-mention-prefix' ),
title = mw.Title.newFromText( word, mw.config.get( 'wgNamespaceIds' ).user );
if ( this.surface.getMode() === 'source' ) {
// TODO: this should be configurable per-wiki so that e.g. custom templates can be used
word = prefix + '[[' + title.getPrefixedText() + '|' + word + ']]';
return MWUsernameCompletionAction.super.prototype.insertCompletion.call( this, word, range );
}
fragment = this.surface.getModel().getLinearFragment( range );
fragment.removeContent().insertContent( [
{ type: 'mwPing', attributes: { user: word } },
{ type: '/mwPing' }
] );
fragment.collapseToEnd();
return fragment;
};
MWUsernameCompletionAction.prototype.shouldAbandon = function ( input ) {
// TODO: need to consider whether pending loads from server are happening here
return MWUsernameCompletionAction.super.prototype.shouldAbandon.apply( this, arguments ) && input.split( /\s+/ ).length > 2;
};
/* Registration */
ve.ui.actionFactory.register( MWUsernameCompletionAction );
openCommand = new ve.ui.Command(
'openMWUsernameCompletions', MWUsernameCompletionAction.static.name, 'open',
{ supportedSelections: [ 'linear' ] }
);
insertAndOpenCommand = new ve.ui.Command(
'insertAndOpenMWUsernameCompletions', MWUsernameCompletionAction.static.name, 'insertAndOpen',
{ supportedSelections: [ 'linear' ] }
);
sequence = new ve.ui.Sequence( 'autocompleteMWUsernames', 'openMWUsernameCompletions', '@', 0 );
ve.ui.commandRegistry.register( openCommand );
ve.ui.commandRegistry.register( insertAndOpenCommand );
ve.ui.wikitextCommandRegistry.register( openCommand );
ve.ui.wikitextCommandRegistry.register( insertAndOpenCommand );
ve.ui.sequenceRegistry.register( sequence );
ve.ui.wikitextSequenceRegistry.register( sequence );
module.exports = MWUsernameCompletionAction;