summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/AbuseFilter/includes/Views/AbuseFilterViewRevert.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/AbuseFilter/includes/Views/AbuseFilterViewRevert.php')
-rw-r--r--www/wiki/extensions/AbuseFilter/includes/Views/AbuseFilterViewRevert.php299
1 files changed, 299 insertions, 0 deletions
diff --git a/www/wiki/extensions/AbuseFilter/includes/Views/AbuseFilterViewRevert.php b/www/wiki/extensions/AbuseFilter/includes/Views/AbuseFilterViewRevert.php
new file mode 100644
index 00000000..ef3773d7
--- /dev/null
+++ b/www/wiki/extensions/AbuseFilter/includes/Views/AbuseFilterViewRevert.php
@@ -0,0 +1,299 @@
+<?php
+
+class AbuseFilterViewRevert extends AbuseFilterView {
+ public $origPeriodStart, $origPeriodEnd, $mPeriodStart, $mPeriodEnd;
+ public $mReason;
+
+ function show() {
+ $lang = $this->getLanguage();
+ $filter = $this->mPage->mFilter;
+
+ $user = $this->getUser();
+ $out = $this->getOutput();
+
+ if ( !$user->isAllowed( 'abusefilter-revert' ) ) {
+ throw new PermissionsError( 'abusefilter-revert' );
+ }
+
+ $this->loadParameters();
+
+ if ( $this->attemptRevert() ) {
+ return;
+ }
+
+ $out->addWikiMsg( 'abusefilter-revert-intro', Message::numParam( $filter ) );
+ $out->setPageTitle( $this->msg( 'abusefilter-revert-title' )->numParams( $filter ) );
+
+ // First, the search form. Limit dates to avoid huge queries
+ $RCMaxAge = $this->getConfig()->get( 'RCMaxAge' );
+ $min = wfTimestamp( TS_ISO_8601, time() - $RCMaxAge );
+ $max = wfTimestampNow();
+ $filterLink =
+ $this->linkRenderer->makeLink(
+ SpecialPage::getTitleFor( 'AbuseFilter', intval( $filter ) ),
+ $lang->formatNum( intval( $filter ) )
+ );
+ $searchFields = [];
+ $searchFields['filterid'] = [
+ 'type' => 'info',
+ 'default' => $filterLink,
+ 'raw' => true,
+ 'label-message' => 'abusefilter-revert-filter'
+ ];
+ $searchFields['periodstart'] = [
+ 'type' => 'datetime',
+ 'name' => 'wpPeriodStart',
+ 'default' => $this->origPeriodStart,
+ 'label-message' => 'abusefilter-revert-periodstart',
+ 'min' => $min,
+ 'max' => $max
+ ];
+ $searchFields['periodend'] = [
+ 'type' => 'datetime',
+ 'name' => 'wpPeriodEnd',
+ 'default' => $this->origPeriodEnd,
+ 'label-message' => 'abusefilter-revert-periodend',
+ 'min' => $min,
+ 'max' => $max
+ ];
+
+ HTMLForm::factory( 'ooui', $searchFields, $this->getContext() )
+ ->addHiddenField( 'submit', 1 )
+ ->setAction( $this->getTitle( "revert/$filter" )->getLocalURL() )
+ ->setWrapperLegendMsg( 'abusefilter-revert-search-legend' )
+ ->setSubmitTextMsg( 'abusefilter-revert-search' )
+ ->setMethod( 'post' )
+ ->prepareForm()
+ ->displayForm( false );
+
+ if ( $this->mSubmit ) {
+ // Add a summary of everything that will be reversed.
+ $out->addWikiMsg( 'abusefilter-revert-preview-intro' );
+
+ // Look up all of them.
+ $results = $this->doLookup();
+ $list = [];
+
+ foreach ( $results as $result ) {
+ $displayActions = array_map(
+ [ 'AbuseFilter', 'getActionDisplay' ],
+ $result['actions'] );
+
+ $msg = $this->msg( 'abusefilter-revert-preview-item' )
+ ->rawParams(
+ $lang->timeanddate( $result['timestamp'], true ),
+ Linker::userLink( $result['userid'], $result['user'] ),
+ $result['action'],
+ $this->linkRenderer->makeLink( $result['title'] ),
+ $lang->commaList( $displayActions ),
+ $this->linkRenderer->makeLink(
+ SpecialPage::getTitleFor( 'AbuseLog' ),
+ $this->msg( 'abusefilter-log-detailslink' )->text(),
+ [],
+ [ 'details' => $result['id'] ]
+ )
+ )->params( $result['user'] )->parse();
+ $list[] = Xml::tags( 'li', null, $msg );
+ }
+
+ $out->addHTML( Xml::tags( 'ul', null, implode( "\n", $list ) ) );
+
+ // Add a button down the bottom.
+ $confirmForm = [];
+ $confirmForm['edittoken'] = [
+ 'type' => 'hidden',
+ 'name' => 'editToken',
+ 'default' => $user->getEditToken( "abusefilter-revert-$filter" )
+ ];
+ $confirmForm['title'] = [
+ 'type' => 'hidden',
+ 'name' => 'title',
+ 'default' => $this->getTitle( "revert/$filter" )->getPrefixedDBkey()
+ ];
+ $confirmForm['wpPeriodStart'] = [
+ 'type' => 'hidden',
+ 'name' => 'wpPeriodStart',
+ 'default' => $this->origPeriodStart
+ ];
+ $confirmForm['wpPeriodEnd'] = [
+ 'type' => 'hidden',
+ 'name' => 'wpPeriodEnd',
+ 'default' => $this->origPeriodEnd
+ ];
+ $confirmForm['reason'] = [
+ 'type' => 'text',
+ 'label-message' => 'abusefilter-revert-reasonfield',
+ 'name' => 'wpReason',
+ 'id' => 'wpReason',
+ ];
+ HTMLForm::factory( 'ooui', $confirmForm, $this->getContext() )
+ ->setAction( $this->getTitle( "revert/$filter" )->getLocalURL() )
+ ->setWrapperLegendMsg( 'abusefilter-revert-confirm-legend' )
+ ->setSubmitTextMsg( 'abusefilter-revert-confirm' )
+ ->setMethod( 'post' )
+ ->prepareForm()
+ ->displayForm( false );
+
+ }
+ }
+
+ function doLookup() {
+ $periodStart = $this->mPeriodStart;
+ $periodEnd = $this->mPeriodEnd;
+ $filter = $this->mPage->mFilter;
+
+ $conds = [ 'afl_filter' => $filter ];
+
+ $dbr = wfGetDB( DB_REPLICA );
+
+ if ( $periodStart ) {
+ $conds[] = 'afl_timestamp>' . $dbr->addQuotes( $dbr->timestamp( $periodStart ) );
+ }
+ if ( $periodEnd ) {
+ $conds[] = 'afl_timestamp<' . $dbr->addQuotes( $dbr->timestamp( $periodEnd ) );
+ }
+
+ // Database query.
+ $res = $dbr->select( 'abuse_filter_log', '*', $conds, __METHOD__ );
+
+ $results = [];
+ foreach ( $res as $row ) {
+ // Don't revert if there was no action, or the action was global
+ if ( !$row->afl_actions || $row->afl_wiki != null ) {
+ continue;
+ }
+
+ $actions = explode( ',', $row->afl_actions );
+ $reversibleActions = [ 'block', 'blockautopromote', 'degroup' ];
+ $currentReversibleActions = array_intersect( $actions, $reversibleActions );
+ if ( count( $currentReversibleActions ) ) {
+ $results[] = [
+ 'id' => $row->afl_id,
+ 'actions' => $currentReversibleActions,
+ 'user' => $row->afl_user_text,
+ 'userid' => $row->afl_user,
+ 'vars' => AbuseFilter::loadVarDump( $row->afl_var_dump ),
+ 'title' => Title::makeTitle( $row->afl_namespace, $row->afl_title ),
+ 'action' => $row->afl_action,
+ 'timestamp' => $row->afl_timestamp
+ ];
+ }
+ }
+
+ return $results;
+ }
+
+ function loadParameters() {
+ $request = $this->getRequest();
+
+ $this->origPeriodStart = $request->getText( 'wpPeriodStart' );
+ $this->mPeriodStart = strtotime( $this->origPeriodStart );
+ $this->origPeriodEnd = $request->getText( 'wpPeriodEnd' );
+ $this->mPeriodEnd = strtotime( $this->origPeriodEnd );
+ $this->mSubmit = $request->getVal( 'submit' );
+ $this->mReason = $request->getVal( 'wpReason' );
+ }
+
+ function attemptRevert() {
+ $filter = $this->mPage->mFilter;
+ $token = $this->getRequest()->getVal( 'editToken' );
+ if ( !$this->getUser()->matchEditToken( $token, "abusefilter-revert-$filter" ) ) {
+ return false;
+ }
+
+ $results = $this->doLookup();
+ foreach ( $results as $result ) {
+ $actions = $result['actions'];
+ foreach ( $actions as $action ) {
+ $this->revertAction( $action, $result );
+ }
+ }
+ $this->getOutput()->wrapWikiMsg(
+ '<p class="success">$1</p>',
+ [
+ 'abusefilter-revert-success',
+ $filter,
+ $this->getLanguage()->formatNum( $filter )
+ ]
+ );
+
+ return true;
+ }
+
+ /**
+ * @param string $action
+ * @param array $result
+ * @return bool
+ * @throws MWException
+ */
+ function revertAction( $action, $result ) {
+ switch ( $action ) {
+ case 'block':
+ $block = Block::newFromTarget( $result['user'] );
+ if ( !( $block && $block->getBy() == AbuseFilter::getFilterUser()->getId() ) ) {
+ // Not blocked by abuse filter
+ return false;
+ }
+ $block->delete();
+ $logEntry = new ManualLogEntry( 'block', 'unblock' );
+ $logEntry->setTarget( Title::makeTitle( NS_USER, $result['user'] ) );
+ $logEntry->setComment(
+ $this->msg(
+ 'abusefilter-revert-reason', $this->mPage->mFilter, $this->mReason
+ )->inContentLanguage()->text()
+ );
+ $logEntry->setPerformer( $this->getUser() );
+ $logEntry->publish( $logEntry->insert() );
+ return true;
+ case 'blockautopromote':
+ ObjectCache::getMainStashInstance()->delete(
+ AbuseFilter::autoPromoteBlockKey( User::newFromId( $result['userid'] ) )
+ );
+ return true;
+ case 'degroup':
+ // Pull the user's groups from the vars.
+ $oldGroups = $result['vars']['USER_GROUPS'];
+ $oldGroups = explode( ',', $oldGroups );
+ $oldGroups = array_diff(
+ $oldGroups,
+ array_intersect( $oldGroups, User::getImplicitGroups() )
+ );
+
+ $rows = [];
+ foreach ( $oldGroups as $group ) {
+ $rows[] = [
+ 'ug_user' => $result['userid'],
+ 'ug_group' => $group
+ ];
+ }
+
+ // Cheat a little bit. User::addGroup repeatedly is too slow.
+ $user = User::newFromId( $result['userid'] );
+ $currentGroups = $user->getGroups();
+ $newGroups = array_merge( $oldGroups, $currentGroups );
+
+ // Don't do anything if there are no groups to add.
+ if ( !count( array_diff( $newGroups, $currentGroups ) ) ) {
+ return false;
+ }
+
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->insert( 'user_groups', $rows, __METHOD__, [ 'IGNORE' ] );
+ $user->invalidateCache();
+
+ $log = new LogPage( 'rights' );
+ $log->addEntry( 'rights', $user->getUserPage(),
+ $this->msg(
+ 'abusefilter-revert-reason',
+ $this->mPage->mFilter,
+ $this->mReason
+ )->inContentLanguage()->text(),
+ [ implode( ',', $currentGroups ), implode( ',', $newGroups ) ]
+ );
+
+ return true;
+ }
+
+ throw new MWException( 'Invalid action' . $action );
+ }
+}