Merge "Use reliability API to detect blocked external links"

This commit is contained in:
jenkins-bot 2024-06-10 18:10:08 +00:00 committed by Gerrit Code Review
commit c90200d24b
7 changed files with 68 additions and 10 deletions

View file

@ -75,6 +75,17 @@ class ApiEditCheckReferenceUrl extends ApiBase {
return $matches !== false;
}
/**
* Check if the required extensions are available for this API to be usable
*
* @return bool
*/
public static function isAvailable(): bool {
return ExtensionRegistry::getInstance()->isLoaded( 'SpamBlacklist' ) ||
// BlockedExternalDomains is within AbuseFilter:
ExtensionRegistry::getInstance()->isLoaded( 'AbuseFilter' );
}
/**
* @inheritDoc
*/

View file

@ -1893,6 +1893,7 @@
"visualeditor-linkinspector-educationpopup-text",
"visualeditor-linkinspector-educationpopup-title",
"visualeditor-linkinspector-illegal-title",
"visualeditor-linkinspector-invalid-blocked",
"visualeditor-linkinspector-invalid-external",
"visualeditor-linknodeinspector-add-label",
"visualeditor-linknodeinspector-title",

View file

@ -275,6 +275,7 @@
"visualeditor-linkinspector-educationpopup-text": "Link important words to other wiki articles or even other websites. It will help readers understand the context.",
"visualeditor-linkinspector-educationpopup-title": "Links",
"visualeditor-linkinspector-illegal-title": "Invalid page title",
"visualeditor-linkinspector-invalid-blocked": "People at this wiki decided to block links to this site. Please try another link.",
"visualeditor-linkinspector-invalid-external": "Enter a full URL, e.g. https://example.org",
"visualeditor-linknodeinspector-add-label": "Add label",
"visualeditor-linknodeinspector-title": "Simple link",

View file

@ -296,6 +296,7 @@
"visualeditor-linkinspector-educationpopup-text": "Text shown in education popup for the link inspector tool.",
"visualeditor-linkinspector-educationpopup-title": "Title shown at the top of education popup for the link inspector tool.\n{{Identical|Link}}",
"visualeditor-linkinspector-illegal-title": "Warning that the entered text is not a valid page title.",
"visualeditor-linkinspector-invalid-blocked": "Warning that the entered URL is blocked",
"visualeditor-linkinspector-invalid-external": "Warning that the entered URL is not valid.",
"visualeditor-linknodeinspector-add-label": "Label of button that converts an auto-numbered, external, labelless link into a labeled external link",
"visualeditor-linknodeinspector-title": "Title of inspector for editing auto-numbered, external, labelless links.\n\nSee also:\n* {{msg-mw|Visualeditor-annotationbutton-linknode-tooltip}}",

View file

@ -25,6 +25,7 @@ use MediaWiki\ChangeTags\Hook\ListDefinedTagsHook;
use MediaWiki\Diff\Hook\DifferenceEngineViewHeaderHook;
use MediaWiki\Diff\Hook\TextSlotDiffRendererTablePrefixHook;
use MediaWiki\EditPage\EditPage;
use MediaWiki\Extension\VisualEditor\EditCheck\ApiEditCheckReferenceUrl;
use MediaWiki\Hook\BeforeInitializeHook;
use MediaWiki\Hook\BeforePageDisplayHook;
use MediaWiki\Hook\CustomEditorHook;
@ -1195,6 +1196,7 @@ class Hooks implements
'editCheckTagging' => $veConfig->get( 'VisualEditorEditCheckTagging' ),
'editCheck' => $veConfig->get( 'VisualEditorEditCheck' ),
'editCheckABTest' => $veConfig->get( 'VisualEditorEditCheckABTest' ),
'editCheckReliabilityAvailable' => ApiEditCheckReferenceUrl::isAvailable(),
'namespacesWithSubpages' => $namespacesWithSubpagesEnabled,
'specialBooksources' => urldecode( SpecialPage::getTitleFor( 'Booksources' )->getPrefixedURL() ),
'rebaserUrl' => $coreConfig->get( 'VisualEditorRebaserURL' ),

View file

@ -88,6 +88,8 @@ ve.ui.MWLinkAnnotationInspector.prototype.initialize = function () {
}
);
this.onExternalLinkInputChangeDebounced = ve.debounce( this.onExternalLinkInputChange, 750 );
// Events
this.linkTypeIndex.connect( this, { set: 'onLinkTypeIndexSet' } );
this.labelInput.connect( this, { change: 'onLabelInputChange' } );
@ -100,7 +102,7 @@ ve.ui.MWLinkAnnotationInspector.prototype.initialize = function () {
enter: 'onLinkInputEnter'
} );
this.externalAnnotationInput.getTextInputWidget().connect( this, {
change: 'onExternalLinkInputChange',
change: 'onExternalLinkInputChangeDebounced',
enter: 'onLinkInputEnter'
} );
// this.internalAnnotationInput is already bound by parent class
@ -301,16 +303,26 @@ ve.ui.MWLinkAnnotationInspector.prototype.onInternalLinkInputChange = function (
* @param {string} value Current value of input widget
*/
ve.ui.MWLinkAnnotationInspector.prototype.onExternalLinkInputChange = function () {
this.externalAnnotationInput.getTextInputWidget().getValidity()
.then(
() => {
this.externalAnnotationField.setErrors( [] );
this.updateSize();
}, () => {
this.externalAnnotationField.setErrors( [ ve.msg( 'visualeditor-linkinspector-invalid-external' ) ] );
this.updateSize();
this.externalAnnotationInput.getValidity().then(
() => {
// clear any invalid-protocol errors
this.externalAnnotationField.setErrors( [] );
}, ( errortype ) => {
// Messages that can be used here:
// * visualeditor-linkinspector-invalid-blocked
// * visualeditor-linkinspector-invalid-external
this.externalAnnotationField.setErrors( [ ve.msg( 'visualeditor-linkinspector-' + errortype ) ] );
if ( errortype === 'invalid-blocked' ) {
// This has been quite async, so:
this.actions.forEach( { actions: [ 'done', 'insert' ] }, ( action ) => {
action.setDisabled( true );
} );
ve.track( 'activity.editCheckReliability', { action: 'link-blocked' } );
}
);
}
).always( () => {
this.updateSize();
} );
if ( this.isActive && !this.trackedExternalLinkInputChange && !this.switchingLinkTypes ) {
ve.track( 'activity.' + this.constructor.static.name, { action: 'external-link-input' } );

View file

@ -72,3 +72,33 @@ ve.ui.MWExternalLinkAnnotationWidget.static.createExternalLinkInputWidget = func
ve.ui.MWExternalLinkAnnotationWidget.prototype.createInputWidget = function ( config ) {
return this.constructor.static.createExternalLinkInputWidget( config );
};
/**
* Get the validity of current value
*
* @see OO.ui.TextInputWidget#getValidity
*
* @return {jQuery.Promise} A promise that resolves if the value is valid,
* rejects if not. If it's rejected, it'll resolve with an error code.
*/
ve.ui.MWExternalLinkAnnotationWidget.prototype.getValidity = function () {
const url = this.input.getValue().trim();
return this.input.getValidity().then(
// input validity check covers whether it's a valid external link, now check whether it's blocked:
() => {
if ( mw.config.get( 'wgVisualEditorConfig' ).editCheckReliabilityAvailable ) {
return ( new mw.Api().get( {
action: 'editcheckreferenceurl',
url: url,
formatversion: 2
} ) ).then( ( reliablityResults ) => {
if ( reliablityResults && reliablityResults.editcheckreferenceurl[ url ] === 'blocked' ) {
return ve.createDeferred().reject( 'invalid-blocked' );
}
} );
}
},
// invalid link, so provide a reason
() => ve.createDeferred().reject( 'invalid-external' )
);
};