mediawiki-extensions-Visual.../modules/ve-mw/ui/widgets/ve.ui.MWTemplateTitleInputWidget.js
Thiemo Kreuz b15455ec3d Fix duplication bug in MWTemplateTitleInputWidget
Template names sometimes show up twice when searching for a
template in the "Add a template" dialog.

This is a bit hard to test. The code responsible for this
is not in a single place. The feature is in the upstream
TitleWidget class. It's not broken. It makes sense to
provide e.g. "foo" and "Foo" as two separate options when
the user typed "foo", but the page is named "Foo". Both are
valid, and the feature allows the user to pick either.

But the VE widget does it's own normalization. Both entries
are normalized to "Foo". Both do the same. The additional
one is pointless.

You can try this on the actual enwiki: Open VE, insert a
template, search for "Template:nHLE".

Change-Id: I65e706c4d131a2f8c605d7979a02ea56f831bf03
2021-05-21 11:43:31 +00:00

175 lines
5.8 KiB
JavaScript

/*!
* VisualEditor UserInterface MWTemplateTitleInputWidget class.
*
* @copyright 2011-2020 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* Creates an ve.ui.MWTemplateTitleInputWidget object.
*
* @class
* @extends mw.widgets.TitleInputWidget
*
* @constructor
* @param {Object} [config] Configuration options
* @cfg {number} [namespace] Namespace to prepend to queries. Defaults to template namespace.
*/
ve.ui.MWTemplateTitleInputWidget = function VeUiMWTemplateTitleInputWidget( config ) {
config = ve.extendObject( {}, {
namespace: mw.config.get( 'wgNamespaceIds' ).template,
// We don't need results to show up twice normalized and unnormalized
addQueryInput: false
}, config );
// Parent constructor
ve.ui.MWTemplateTitleInputWidget.super.call( this, config );
this.showTemplateDescriptions = this.showDescriptions;
// Clear the showDescriptions flag for subsequent requests as we implement
// description fetch ourselves
this.showDescriptions = false;
// Properties
this.descriptions = {};
// Initialization
this.$element.addClass( 've-ui-mwTemplateTitleInputWidget' );
};
/* Inheritance */
// FIXME: This should extend mw.widgets.TitleSearchWidget instead
OO.inheritClass( ve.ui.MWTemplateTitleInputWidget, mw.widgets.TitleInputWidget );
/* Methods */
// @inheritdoc mw.widgets.TitleWidget
ve.ui.MWTemplateTitleInputWidget.prototype.getApiParams = function ( query ) {
var params = ve.ui.MWTemplateTitleInputWidget.super.prototype.getApiParams.call( this, query );
// TODO: This should stay as a feature flag for 3rd-parties to fallback to prefixsearch
if ( mw.config.get( 'wgVisualEditorConfig' ).cirrusSearchLookup ) {
params.generator = 'search';
params.gsrsearch = params.gpssearch;
params.gsrsearch += '*';
// params.gsrsort = 'incoming_links_desc';
params.gsrnamespace = params.gpsnamespace;
params.gsrlimit = params.gpslimit;
delete params.gpssearch;
delete params.gpsnamespace;
delete params.gpslimit;
}
return params;
};
// @inheritdoc mw.widgets.TitleInputWidget
ve.ui.MWTemplateTitleInputWidget.prototype.getLookupRequest = function () {
var widget = this,
originalResponse,
templateDataMessage = mw.message( 'templatedata-doc-subpage' ),
templateDataInstalled = templateDataMessage.exists(),
templateDocPageFragment = '/' + templateDataMessage.text(),
promise = ve.ui.MWTemplateTitleInputWidget.super.prototype.getLookupRequest.call( this );
if ( !this.showTemplateDescriptions ) {
return promise;
}
return promise
.then( function ( response ) {
var xhr, pageId, redirIndex,
redirects = ( response.query && response.query.redirects ) || {},
origPages = ( response.query && response.query.pages ) || {},
newPages = [],
titles = [];
// Build a new array to replace response.query.pages, ensuring everything goes into
// the order defined by the page's index key, instead of whatever random order the
// browser would let you iterate over the old object in.
for ( pageId in origPages ) {
if ( 'index' in origPages[ pageId ] ) {
newPages[ origPages[ pageId ].index - 1 ] = origPages[ pageId ];
} else {
// Watch out for cases where the index is specified on the redirect object
// rather than the page object.
for ( redirIndex in redirects ) {
if ( redirects[ redirIndex ].to === origPages[ pageId ].title ) {
newPages[ redirects[ redirIndex ].index - 1 ] = origPages[ pageId ];
break;
}
}
}
}
// T54448: Filter out matches which end in /doc or as configured on-wiki
if ( templateDataInstalled ) {
newPages = newPages.filter( function ( page ) {
// Can't use String.endsWith() as that's ES6.
// page.title.endsWith( templateDocPageFragment )
return page.title.slice( 0 - templateDocPageFragment.length ) !== templateDocPageFragment;
} );
} else {
// Even if not filtering /doc, collapse the sparse array
newPages = newPages.filter( function ( page ) {
return page;
} );
}
titles = newPages.map( function ( page ) {
return page.title;
} );
ve.setProp( response, 'query', 'pages', newPages );
originalResponse = response; // lie!
// Also get descriptions
// FIXME: This should go through MWTransclusionModel rather than duplicate.
if ( titles.length > 0 ) {
xhr = widget.getApi().get( {
action: 'templatedata',
format: 'json',
formatversion: 2,
titles: titles,
redirects: !!widget.showRedirects,
includeMissingTitles: '1',
lang: mw.config.get( 'wgUserLanguage' )
} );
return xhr.promise( { abort: xhr.abort } );
}
} )
.then( function ( templateDataResponse ) {
var index, page, missingTitle,
pages = ( templateDataResponse && templateDataResponse.pages ) || {};
// Look for descriptions and cache them
for ( index in pages ) {
page = pages[ index ];
if ( page.missing ) {
// Remmeber templates that don't exist in the link cache
// { title: { missing: true|false }
missingTitle = {};
missingTitle[ page.title ] = { missing: true };
ve.init.platform.linkCache.setMissing( missingTitle );
} else if ( !page.notemplatedata ) {
// Cache descriptions
widget.descriptions[ page.title ] = page.description;
}
}
// Return the original response
return originalResponse;
}, function () {
// API request failed; most likely, we're on a wiki which doesn't have TemplateData.
return originalResponse;
} )
.promise( { abort: function () {} } );
};
// @inheritdoc mw.widgets.TitleInputWidget
ve.ui.MWTemplateTitleInputWidget.prototype.getOptionWidgetData = function ( title ) {
return ve.extendObject(
ve.ui.MWTemplateTitleInputWidget.super.prototype.getOptionWidgetData.apply( this, arguments ),
{ description: this.descriptions[ title ] }
);
};