mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/AbuseFilter.git
synced 2024-12-01 00:56:26 +00:00
45d80bc7e5
- Depend on a generic IContextSource rather than SpecialAbuseFilter (lower coupling); - Inject a LinkRenderer (IContextSource doesn't have a ::getLinkRenderer method) - Add a helper method in SpecialAbuseFilter to get the page title, that can also be used elsewhere (and the name constant can be made private now) - Pull down the mFilter property (and rename it to just 'filter') to classes that actually need it. Some classes didn't need this at all and the types were different among subclasses Now the only cause of coupling between the View classes and SpecialAbuseFilter is the static call in getTitle. Change-Id: I3df0c3a7621f0cc9a64a16b0a402a15aae2d5d73
319 lines
8.7 KiB
PHP
319 lines
8.7 KiB
PHP
<?php
|
|
|
|
use MediaWiki\Extension\AbuseFilter\AbuseFilterServices;
|
|
use MediaWiki\Linker\LinkRenderer;
|
|
use Wikimedia\Rdbms\IDatabase;
|
|
|
|
abstract class AbuseFilterView extends ContextSource {
|
|
/**
|
|
* @var bool Whether the form was submitted
|
|
*/
|
|
public $mSubmit;
|
|
/**
|
|
* @var array The parameters of the current request
|
|
*/
|
|
public $mParams;
|
|
|
|
/**
|
|
* @var LinkRenderer
|
|
*/
|
|
protected $linkRenderer;
|
|
|
|
/**
|
|
* @param IContextSource $context
|
|
* @param LinkRenderer $linkRenderer
|
|
* @param array $params
|
|
*/
|
|
public function __construct( IContextSource $context, LinkRenderer $linkRenderer, array $params ) {
|
|
$this->mParams = $params;
|
|
$this->setContext( $context );
|
|
$this->linkRenderer = $linkRenderer;
|
|
}
|
|
|
|
/**
|
|
* @param string|int $subpage
|
|
* @return Title
|
|
*/
|
|
public function getTitle( $subpage = '' ) {
|
|
return SpecialAbuseFilter::getTitleForSubpage( $subpage );
|
|
}
|
|
|
|
/**
|
|
* Function to show the page
|
|
*/
|
|
abstract public function show();
|
|
|
|
/**
|
|
* @param string $rules
|
|
* @param bool $addResultDiv
|
|
* @param bool $externalForm
|
|
* @param bool $needsModifyRights
|
|
* @param-taint $rules none
|
|
* @return string
|
|
*/
|
|
protected function buildEditBox(
|
|
$rules,
|
|
$addResultDiv = true,
|
|
$externalForm = false,
|
|
$needsModifyRights = true
|
|
) {
|
|
$this->getOutput()->enableOOUI();
|
|
$user = $this->getUser();
|
|
$afPermManager = AbuseFilterServices::getPermissionManager();
|
|
|
|
// Rules are in English
|
|
$editorAttribs = [ 'dir' => 'ltr' ];
|
|
|
|
$noTestAttrib = [];
|
|
$isUserAllowed = $needsModifyRights ?
|
|
$afPermManager->canEdit( $user ) :
|
|
$afPermManager->canViewPrivateFilters( $user );
|
|
if ( !$isUserAllowed ) {
|
|
$noTestAttrib['disabled'] = 'disabled';
|
|
$addResultDiv = false;
|
|
}
|
|
|
|
$rules = rtrim( $rules ) . "\n";
|
|
$switchEditor = null;
|
|
|
|
$rulesContainer = '';
|
|
if ( ExtensionRegistry::getInstance()->isLoaded( 'CodeEditor' ) ) {
|
|
$aceAttribs = [
|
|
'name' => 'wpAceFilterEditor',
|
|
'id' => 'wpAceFilterEditor',
|
|
'class' => 'mw-abusefilter-editor'
|
|
];
|
|
$attribs = array_merge( $editorAttribs, $aceAttribs );
|
|
|
|
$switchEditor =
|
|
new OOUI\ButtonWidget(
|
|
[
|
|
'label' => $this->msg( 'abusefilter-edit-switch-editor' )->text(),
|
|
'id' => 'mw-abusefilter-switcheditor'
|
|
] + $noTestAttrib
|
|
);
|
|
|
|
$rulesContainer .= Xml::element( 'div', $attribs, $rules );
|
|
|
|
// Add Ace configuration variable
|
|
$editorConfig = AbuseFilter::getAceConfig( $isUserAllowed );
|
|
$this->getOutput()->addJsConfigVars( 'aceConfig', $editorConfig );
|
|
}
|
|
|
|
// Build a dummy textarea to be used: for submitting form if CodeEditor isn't installed,
|
|
// and in case JS is disabled (with or without CodeEditor)
|
|
if ( !$isUserAllowed ) {
|
|
$editorAttribs['readonly'] = 'readonly';
|
|
}
|
|
if ( $externalForm ) {
|
|
$editorAttribs['form'] = 'wpFilterForm';
|
|
}
|
|
$rulesContainer .= Xml::textarea( 'wpFilterRules', $rules, 40, 15, $editorAttribs );
|
|
|
|
if ( $isUserAllowed ) {
|
|
$keywordsManager = AbuseFilterServices::getKeywordsManager();
|
|
// Generate builder drop-down
|
|
$rawDropDown = $keywordsManager->getBuilderValues();
|
|
|
|
// The array needs to be rearranged to be understood by OOUI. It comes with the format
|
|
// [ group-msg-key => [ text-to-add => text-msg-key ] ] and we need it as
|
|
// [ group-msg => [ text-msg => text-to-add ] ]
|
|
// Also, the 'other' element must be the first one.
|
|
$dropDownOptions = [ $this->msg( 'abusefilter-edit-builder-select' )->text() => 'other' ];
|
|
foreach ( $rawDropDown as $group => $values ) {
|
|
// Give grep a chance to find the usages:
|
|
// abusefilter-edit-builder-group-op-arithmetic, abusefilter-edit-builder-group-op-comparison,
|
|
// abusefilter-edit-builder-group-op-bool, abusefilter-edit-builder-group-misc,
|
|
// abusefilter-edit-builder-group-funcs, abusefilter-edit-builder-group-vars
|
|
$localisedGroup = $this->msg( "abusefilter-edit-builder-group-$group" )->text();
|
|
$dropDownOptions[ $localisedGroup ] = array_flip( $values );
|
|
$newKeys = array_map(
|
|
function ( $key ) use ( $group ) {
|
|
return $this->msg( "abusefilter-edit-builder-$group-$key" )->text();
|
|
},
|
|
array_keys( $dropDownOptions[ $localisedGroup ] )
|
|
);
|
|
$dropDownOptions[ $localisedGroup ] = array_combine(
|
|
$newKeys, $dropDownOptions[ $localisedGroup ] );
|
|
}
|
|
|
|
$dropDownList = Xml::listDropDownOptionsOoui( $dropDownOptions );
|
|
$dropDown = new OOUI\DropdownInputWidget( [
|
|
'name' => 'wpFilterBuilder',
|
|
'inputId' => 'wpFilterBuilder',
|
|
'options' => $dropDownList
|
|
] );
|
|
|
|
$formElements = [ new OOUI\FieldLayout( $dropDown ) ];
|
|
|
|
// Button for syntax check
|
|
$syntaxCheck =
|
|
new OOUI\ButtonWidget(
|
|
[
|
|
'label' => $this->msg( 'abusefilter-edit-check' )->text(),
|
|
'id' => 'mw-abusefilter-syntaxcheck'
|
|
] + $noTestAttrib
|
|
);
|
|
|
|
// Button for switching editor (if Ace is used)
|
|
if ( $switchEditor !== null ) {
|
|
$formElements[] = new OOUI\FieldLayout(
|
|
new OOUI\Widget( [
|
|
'content' => new OOUI\HorizontalLayout( [
|
|
'items' => [ $switchEditor, $syntaxCheck ]
|
|
] )
|
|
] )
|
|
);
|
|
} else {
|
|
$formElements[] = new OOUI\FieldLayout( $syntaxCheck );
|
|
}
|
|
|
|
$fieldSet = new OOUI\FieldsetLayout( [
|
|
'items' => $formElements,
|
|
'classes' => [ 'mw-abusefilter-edit-buttons', 'mw-abusefilter-javascript-tools' ]
|
|
] );
|
|
|
|
$rulesContainer .= $fieldSet;
|
|
}
|
|
|
|
if ( $addResultDiv ) {
|
|
$rulesContainer .= Xml::element( 'div',
|
|
[ 'id' => 'mw-abusefilter-syntaxresult', 'style' => 'display: none;' ],
|
|
' ' );
|
|
}
|
|
|
|
// Add script
|
|
$this->getOutput()->addModules( 'ext.abuseFilter.edit' );
|
|
|
|
return $rulesContainer;
|
|
}
|
|
|
|
/**
|
|
* Build input and button for loading a filter
|
|
*
|
|
* @return string
|
|
*/
|
|
public function buildFilterLoader() {
|
|
$loadText =
|
|
new OOUI\TextInputWidget(
|
|
[
|
|
'type' => 'number',
|
|
'name' => 'wpInsertFilter',
|
|
'id' => 'mw-abusefilter-load-filter'
|
|
]
|
|
);
|
|
$loadButton =
|
|
new OOUI\ButtonWidget(
|
|
[
|
|
'label' => $this->msg( 'abusefilter-test-load' )->text(),
|
|
'id' => 'mw-abusefilter-load'
|
|
]
|
|
);
|
|
$loadGroup =
|
|
new OOUI\ActionFieldLayout(
|
|
$loadText,
|
|
$loadButton,
|
|
[
|
|
'label' => $this->msg( 'abusefilter-test-load-filter' )->text()
|
|
]
|
|
);
|
|
// CSS class for reducing default input field width
|
|
$loadDiv =
|
|
Xml::tags(
|
|
'div',
|
|
[ 'class' => 'mw-abusefilter-load-filter-id' ],
|
|
$loadGroup
|
|
);
|
|
return $loadDiv;
|
|
}
|
|
|
|
/**
|
|
* @param IDatabase $db
|
|
* @param string|false $action 'edit', 'move', 'createaccount', 'delete' or false for all
|
|
* @return string
|
|
*/
|
|
public function buildTestConditions( IDatabase $db, $action = false ) {
|
|
// If one of these is true, we're abusefilter compatible.
|
|
switch ( $action ) {
|
|
case 'edit':
|
|
return $db->makeList( [
|
|
// Actually, this is only one condition, but this way we get it as string
|
|
'rc_source' => [
|
|
RecentChange::SRC_EDIT,
|
|
RecentChange::SRC_NEW,
|
|
]
|
|
], LIST_AND );
|
|
case 'move':
|
|
return $db->makeList( [
|
|
'rc_source' => RecentChange::SRC_LOG,
|
|
'rc_log_type' => 'move',
|
|
'rc_log_action' => 'move'
|
|
], LIST_AND );
|
|
case 'createaccount':
|
|
return $db->makeList( [
|
|
'rc_source' => RecentChange::SRC_LOG,
|
|
'rc_log_type' => 'newusers',
|
|
'rc_log_action' => [ 'create', 'autocreate' ]
|
|
], LIST_AND );
|
|
case 'delete':
|
|
return $db->makeList( [
|
|
'rc_source' => RecentChange::SRC_LOG,
|
|
'rc_log_type' => 'delete',
|
|
'rc_log_action' => 'delete'
|
|
], LIST_AND );
|
|
case 'upload':
|
|
return $db->makeList( [
|
|
'rc_source' => RecentChange::SRC_LOG,
|
|
'rc_log_type' => 'upload',
|
|
'rc_log_action' => [ 'upload', 'overwrite', 'revert' ]
|
|
], LIST_AND );
|
|
case false:
|
|
// Done later
|
|
break;
|
|
default:
|
|
// @phan-suppress-next-line PhanTypeSuspiciousStringExpression False does not reach here
|
|
throw new MWException( __METHOD__ . ' called with invalid action: ' . $action );
|
|
}
|
|
|
|
return $db->makeList( [
|
|
'rc_source' => [
|
|
RecentChange::SRC_EDIT,
|
|
RecentChange::SRC_NEW,
|
|
],
|
|
$db->makeList( [
|
|
'rc_source' => RecentChange::SRC_LOG,
|
|
$db->makeList( [
|
|
$db->makeList( [
|
|
'rc_log_type' => 'move',
|
|
'rc_log_action' => 'move'
|
|
], LIST_AND ),
|
|
$db->makeList( [
|
|
'rc_log_type' => 'newusers',
|
|
'rc_log_action' => [ 'create', 'autocreate' ]
|
|
], LIST_AND ),
|
|
$db->makeList( [
|
|
'rc_log_type' => 'delete',
|
|
'rc_log_action' => 'delete'
|
|
], LIST_AND ),
|
|
$db->makeList( [
|
|
'rc_log_type' => 'upload',
|
|
'rc_log_action' => [ 'upload', 'overwrite', 'revert' ]
|
|
], LIST_AND ),
|
|
], LIST_OR ),
|
|
], LIST_AND ),
|
|
], LIST_OR );
|
|
}
|
|
|
|
/**
|
|
* @param string|int $id
|
|
* @param string|null $text
|
|
* @return string HTML
|
|
*/
|
|
public function getLinkToLatestDiff( $id, $text = null ) {
|
|
return $this->linkRenderer->makeKnownLink(
|
|
$this->getTitle( "history/$id/diff/prev/cur" ),
|
|
$text
|
|
);
|
|
}
|
|
}
|