Cenarium has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/317324

Change subject: Deferred changes blocks
......................................................................

Deferred changes blocks

This enables the AbuseFilter and bots to actively defer
for review all edits by a user.
Such a block can be removed with Special:DeferUnblock.

Change-Id: I22a51065191c9c863f33f5116b6aa948c43e91d1
---
M FlaggedRevs.setup.php
A api/actions/ApiDeferBlock.php
M backend/FlaggedRevs.hooks.php
M backend/FlaggedRevsLog.php
M frontend/FlaggedRevsUI.setup.php
A frontend/specialpages/actions/SpecialDeferUnblock.php
M i18n/flaggedrevs/en.json
M i18n/flaggedrevs/qqq.json
8 files changed, 279 insertions(+), 17 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/FlaggedRevs 
refs/changes/24/317324/1

diff --git a/FlaggedRevs.setup.php b/FlaggedRevs.setup.php
index eaea917..ced5f57 100755
--- a/FlaggedRevs.setup.php
+++ b/FlaggedRevs.setup.php
@@ -145,6 +145,8 @@
                $classes['ValidationStatistics'] = 
"$spReportDir/ValidationStatistics_body.php";
                $messagesDirs['ValidationStatistics'] = __DIR__ . 
'/i18n/validationstatistics';
                $messagesFiles['ValidationStatistics'] = 
"$langDir/ValidationStatistics.i18n.php";
+               # DeferUnblock
+               $classes['SpecialDeferUnblock'] = 
"$spActionDir/SpecialDeferUnblock.php";
                ### End ###
 
                ### API classes ###
@@ -152,6 +154,8 @@
                $classes['ApiReview'] = "$apiActionDir/ApiReview.php";
                # Defer module for API
                $classes['ApiDefer'] = "$apiActionDir/ApiDefer.php";
+               # Defer block module for API
+               $classes['ApiDeferBlock'] = "$apiActionDir/ApiDeferBlock.php";
                # Page review activity module for API
                $classes['ApiReviewActivity'] = 
"$apiActionDir/ApiReviewActivity.php";
                # Stability config module for API
@@ -217,7 +221,7 @@
                # Autopromote Editors
                $wgHooks['PageContentSaveComplete'][] = 
'FlaggedRevsHooks::onPageContentSaveComplete';
                # Auto-reviewing
-               $wgHooks['RecentChange_save'][] = 
'FlaggedRevsHooks::autoMarkPatrolled';
+               $wgHooks['RecentChange_save'][] = 
'FlaggedRevsHooks::onRecentChangeSave';
                $wgHooks['NewRevisionFromEditComplete'][] = 
'FlaggedRevsHooks::maybeMakeEditReviewed';
                # Null edit review via checkbox
                $wgHooks['PageContentSaveComplete'][] = 
'FlaggedRevsHooks::maybeNullEditReview';
@@ -398,6 +402,7 @@
                if ( $wgFlaggedRevsDeferral ) {
                        $wgAPIListModules['unreviewedpages'] = 
'ApiQueryUnreviewedpages';
                        $wgAPIModules['defer'] = 'ApiDefer';
+                       $wgAPIModules['deferblock'] = 'ApiDeferBlock';
                }
                if ( !$wgFlaggedRevsDeferral && !$wgFlaggedRevsProtection ) {
                        $wgAPIModules['stabilize'] = 'ApiStabilizeGeneral';
@@ -486,8 +491,9 @@
                        return;
                }
                global $wgAbuseFilterActions, 
$wgAbuseFilterCustomActionsHandlers;
-               $wgAbuseFilterActions += [ 'passivedefer' => true, 
'activedefer' => true ];
+               $wgAbuseFilterActions += [ 'passivedefer' => true, 
'activedefer' => true, 'deferblock' => true ];
                $wgAbuseFilterCustomActionsHandlers['passivedefer'] = 
'FlaggedRevsHooks::executeAbuseFilterDefer';
                $wgAbuseFilterCustomActionsHandlers['activedefer'] = 
'FlaggedRevsHooks::executeAbuseFilterDefer';
+               $wgAbuseFilterCustomActionsHandlers['deferblock'] = 
'FlaggedRevsHooks::saveAbuseFilterDeferBlock';
        }
 }
diff --git a/api/actions/ApiDeferBlock.php b/api/actions/ApiDeferBlock.php
new file mode 100644
index 0000000..bc3f6fb
--- /dev/null
+++ b/api/actions/ApiDeferBlock.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * API module that facilitates the deferred changes blocking of users. 
Requires API write mode
+ * to be enabled.
+ *
+ * @ingroup API
+ */
+class ApiDeferBlock extends ApiBase {
+
+       public function execute() {
+               global $wgContLang;
+
+               $user = $this->getUser();
+               $params = $this->extractRequestParams();
+
+               if ( !$user->isAllowed( 'defer' ) || !$user->isAllowed( 
'rollback' ) ) {
+                       $this->dieUsageMsg( 'cantblock' );
+               }
+
+               $target = User::newFromName( $params['user'] );
+               if ( $target instanceof User &&
+                       ( $target->isAnon() /* doesn't exist */ || 
!User::isUsableName( $target->getName() ) )
+               ) {
+                       $this->dieUsageMsg( [ 'nosuchuser', $params['user'] ] );
+               }
+               if ( $target instanceof User && $target->isAllowed( 
'autoreview' ) ) {
+                       $this->dieStatus( Status::newFatal( 
'flaggedrevs-defer-noautoreviewed' ) );
+               }
+
+               list( $target, /* type */ ) = Block::parseTarget( 
$params['user'] );
+               $blockPeriod = (int)mt_rand( 0.5 * 86400, 1.5 * 86400 ); // 
Block for more or less a day.
+
+               ObjectCache::getMainStashInstance()->set(
+                       wfMemcKey( 'flaggedrevs', 'defer-block', $target ), 
true, $blockPeriod
+               );
+               FlaggedRevsLog::updateDeferBlockLog( $user, $target, 
'deferblock', $params['reason'] );
+
+               $this->getResult()->addValue( null, $this->getModuleName(), 
array() );
+       }
+
+       public function mustBePosted() {
+               return true;
+       }
+
+       public function isWriteMode() {
+               return true;
+       }
+
+       public function getAllowedParams() {
+               return [
+                       'user' => [
+                               ApiBase::PARAM_TYPE => 'user',
+                               ApiBase::PARAM_REQUIRED => true
+                       ],
+                       'reason' => '',
+               ];
+       }
+
+       public function needsToken() {
+               return 'csrf';
+       }
+}
diff --git a/backend/FlaggedRevs.hooks.php b/backend/FlaggedRevs.hooks.php
index 849358b..6be9052 100755
--- a/backend/FlaggedRevs.hooks.php
+++ b/backend/FlaggedRevs.hooks.php
@@ -752,27 +752,45 @@
        }
 
        /**
-        * Mark auto-reviewed edits as patrolled
+        * Mark auto-reviewed edits as patrolled, defer if user is deferred 
changes - blocked
         */
-       public static function autoMarkPatrolled( RecentChange &$rc ) {
+       public static function onRecentChangeSave( RecentChange &$rc ) {
                if ( empty( $rc->mAttribs['rc_this_oldid'] ) ) {
                        return true;
                }
                $fa = FlaggableWikiPage::getTitleInstance( $rc->getTitle() );
                $fa->loadPageData( 'fromdbmaster' );
                // Is the page reviewable?
-               if ( $fa->isReviewable() ) {
-                       $revId = $rc->mAttribs['rc_this_oldid'];
-                       // If the edit we just made was reviewed, then it's the 
stable rev
-                       $frev = FlaggedRevision::newFromTitle( $rc->getTitle(), 
$revId, FR_MASTER );
-                       // Reviewed => patrolled
-                       if ( $frev ) {
-                               DeferredUpdates::addCallableUpdate( function () 
use ( $rc, $frev ) {
-                                       
RevisionReviewForm::updateRecentChanges( $rc, 'patrol', $frev );
-                               } );
-                               $rc->mAttribs['rc_patrolled'] = 1; // make sure 
irc/email notifs know status
+               if ( FlaggedRevs::inReviewNamespace( $rc->getTitle() ) ) {
+                       if ( $fa->isReviewable() ) {
+                               $revId = $rc->mAttribs['rc_this_oldid'];
+                               // If the edit we just made was reviewed, then 
it's the stable rev
+                               $frev = FlaggedRevision::newFromTitle( 
$rc->getTitle(), $revId, FR_MASTER );
+                               // Reviewed => patrolled
+                               if ( $frev ) {
+                                       DeferredUpdates::addCallableUpdate( 
function () use ( $rc, $frev ) {
+                                               
RevisionReviewForm::updateRecentChanges( $rc, 'patrol', $frev );
+                                       } );
+                                       $rc->mAttribs['rc_patrolled'] = 1; // 
make sure irc/email notifs know status
+                               }
                        }
-                       return true;
+                       if ( class_exists( 'AbuseFilter' ) && 
FlaggedRevs::useOnlyIfDeferred() ) {
+                               $user = $rc->getPerformer();
+                               $key = wfMemcKey( 'flaggedrevs', 'defer-block', 
$user->getName() );
+                               $blocked = (bool)ObjectCache::getInstance( 
'hash' )->getWithSetCallback(
+                                       $key,
+                                       30,
+                                       function () use ( $key ) {
+                                               return 
(int)ObjectCache::getMainStashInstance()->get( $key );
+                                       }
+                               );
+                               if ( $blocked ) {
+                                       // DeferredUpdates::addCallableUpdate( 
function () use ( $fa, $user ) {
+                                       $reason = wfMessage( 
'flaggedrevs-deferred-blocked' )->text();
+                                       $fa->defer( true, null, $user, $reason 
);
+                                       // } );
+                               }
+                       }
                }
                return true;
        }
@@ -1277,4 +1295,26 @@
                        $article->defer( $override, null, $wgUser, $summary );
                } );
        }
+
+       /**
+        * Save a deferred changes block requested by AbuseFilter
+        * Note: this should be a temporary measure until a sysop is available.
+        */
+       public static function saveAbuseFilterDeferBlock( $action, $params, 
$title,
+               $vars, $rule_desc, $rule_number
+       ) {
+               global $wgUser;
+               if ( $wgUser->isAllowed( 'autoreview' ) ) {
+                       return; // can't defer those users
+               }
+               $userName = $wgUser->getName();
+               $blockPeriod = (int)mt_rand( 0.5 * 86400, 1.5 * 86400 ); // 
Block for more or less a day.
+               ObjectCache::getMainStashInstance()->set(
+                       wfMemcKey( 'flaggedrevs', 'defer-block', $userName ), 
true, $blockPeriod
+               );
+               $performer = AbuseFilter::getFilterUser();
+               $msg = wfMessage( 'abusefilter-actionsummary-deferblock' );
+               $summary = $msg->params( $rule_number, $rule_desc 
)->inContentLanguage()->text();
+               FlaggedRevsLog::updateDeferBlockLog( $performer, $userName, 
'deferblock', $summary );
+       }
 }
diff --git a/backend/FlaggedRevsLog.php b/backend/FlaggedRevsLog.php
index 6af2367..7bc523d 100644
--- a/backend/FlaggedRevsLog.php
+++ b/backend/FlaggedRevsLog.php
@@ -186,6 +186,22 @@
        }
 
        /**
+        * Record a log entry on the block log for a defer block
+        * @param User $performer
+        * @param string $target
+        * @param string $action
+        * @param string $reason
+        */
+       public static function updateDeferBlockLog( User $performer, $target, 
$action, $reason = '' ) {
+               $logEntry = new ManualLogEntry( 'block', $action );
+               $logEntry->setPerformer( $performer );
+               $logEntry->setTarget( Title::makeTitle( NS_USER, $target ) );
+               $logEntry->setComment( $reason );
+               $logId = $logEntry->insert();
+               $logEntry->publish( $logId );
+       }
+
+       /**
         * Expand a list of log params into an associative array
         * For legacy log entries
         * @param array $pars
diff --git a/frontend/FlaggedRevsUI.setup.php b/frontend/FlaggedRevsUI.setup.php
index 9060630..06fd34b 100644
--- a/frontend/FlaggedRevsUI.setup.php
+++ b/frontend/FlaggedRevsUI.setup.php
@@ -123,6 +123,9 @@
                                $pages['ConfiguredPages'] = 'ConfiguredPages';
                                $pages['Stabilization'] = 'Stabilization'; // 
unlisted
                        }
+                       if ( $wgFlaggedRevsDeferral ) {
+                               $pages['DeferUnblock'] = 'SpecialDeferUnblock';
+                       }
                }
        }
 
@@ -235,6 +238,9 @@
                $logActionsHandlers['defer/passive-auto'] = 
'FlaggedRevsDeferLogFormatter'; // edit passively deferred (auto)
                $logActionsHandlers['defer/active-auto'] = 
'FlaggedRevsDeferLogFormatter'; // edit actively deferred (auto)
 
+               $logActionsHandlers['block/deferblock'] = 'BlockLogFormatter'; 
// deferred block
+               $logActionsHandlers['block/deferunblock'] = 
'BlockLogFormatter'; // deferred unblock
+
                # B/C ...
                $logActions['rights/erevoke']  = 'rights-editor-revoke';
        }
diff --git a/frontend/specialpages/actions/SpecialDeferUnblock.php 
b/frontend/specialpages/actions/SpecialDeferUnblock.php
new file mode 100644
index 0000000..08d87e3
--- /dev/null
+++ b/frontend/specialpages/actions/SpecialDeferUnblock.php
@@ -0,0 +1,118 @@
+<?php
+/**
+ * A special page for unblocking users from deferred changes blocks
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialDeferUnblock extends SpecialPage {
+
+       protected $target;
+
+       public function __construct() {
+               parent::__construct( 'DeferUnblock', 'block' );
+       }
+
+       public function doesWrites() {
+               return true;
+       }
+
+       public function execute( $par ) {
+               $this->checkPermissions();
+               $this->checkReadOnly();
+
+               list( $this->target, /* type */ ) = 
SpecialBlock::getTargetAndType( $par, $this->getRequest() );
+               if ( $this->target instanceof User ) {
+                       # Set the 'relevant user' in the skin, so it displays 
links like Contributions,
+                       # User logs, UserRights, etc.
+                       $this->getSkin()->setRelevantUser( $this->target );
+               }
+
+               $this->setHeaders();
+               $this->outputHeader();
+
+               $out = $this->getOutput();
+               $out->setPageTitle( $this->msg( 'unblockip' ) );
+               $out->addModules( [ 'mediawiki.special', 
'mediawiki.userSuggest' ] );
+
+               $form = new HTMLForm( $this->getFields(), $this->getContext() );
+               $form->setWrapperLegendMsg( 'unblockip' );
+               $form->setSubmitCallback( [ __CLASS__, 'processUIUnblock' ] );
+               $form->setSubmitTextMsg( 'ipusubmit' );
+               $form->addPreText( $this->msg( 'flaggedrevs-deferunblock' 
)->parseAsBlock() );
+
+               if ( $form->show() ) {
+                       $out->addWikiMsg( 'unblocked-ip', wfEscapeWikiText( 
$this->target ) );
+               }
+       }
+
+       /**
+        * Submit callback for an HTMLForm object
+        * @param array $data
+        * @param HTMLForm $form
+        * @return array|bool Array(message key, parameters)
+        */
+       public static function processUIUnblock( array $data, HTMLForm $form ) {
+               return self::processUnblock( $data, $form->getContext() );
+       }
+
+       /**
+        * Process the form
+        *
+        * @param array $data
+        * @param IContextSource $context
+        * @return array|bool Array(message key, parameters) on failure, True 
on success
+        */
+       public static function processUnblock( array $data, IContextSource 
$context ) {
+               $performer = $context->getUser();
+               $target = $data['Target'];
+               list( $target, /* type */ ) = Block::parseTarget( $target );
+               $key = wfMemcKey( 'flaggedrevs', 'defer-block', $target );
+               $stashInstance = ObjectCache::getMainStashInstance();
+               if ( !$stashInstance->get( $key ) ) {
+                       return [ [ 'ipb_cant_unblock', $target ] ];
+               }
+               $stashInstance->delete( $key );
+               ObjectCache::getInstance( 'hash' )->delete( $key );
+               FlaggedRevsLog::updateDeferBlockLog( $performer, $target, 
'deferunblock', $data['Reason'] );
+               return true;
+       }
+
+       protected function getFields() {
+               return [
+                       'Target' => [
+                               'type' => 'text',
+                               'label-message' => 'ipaddressorusername',
+                               'autofocus' => true,
+                               'size' => '45',
+                               'required' => true,
+                               'cssclass' => 'mw-autocomplete-user', // used 
by mediawiki.userSuggest
+                       ],
+                       'Reason' => [
+                               'type' => 'text',
+                               'label-message' => 'ipbreason',
+                       ]
+               ];
+       }
+
+       /**
+        * Return an array of subpages beginning with $search that this special 
page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return [];
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, 
$offset );
+       }
+
+       protected function getGroupName() {
+               return 'users';
+       }
+}
diff --git a/i18n/flaggedrevs/en.json b/i18n/flaggedrevs/en.json
index 91cf1bc..84bd66b 100644
--- a/i18n/flaggedrevs/en.json
+++ b/i18n/flaggedrevs/en.json
@@ -231,13 +231,20 @@
        "flaggedrevs-defer-notlatestauthor": "Someone else has edited the 
page.",
        "abusefilter-edit-action-passivedefer": "Defer this edit for review, 
display it to readers",
        "abusefilter-edit-action-activedefer": "Defer this edit for review, 
don't display it to readers",
+       "abusefilter-edit-action-deferblock": "Defer all edits by the user 
(revert with Special:DeferUnblock)",
        "abusefilter-action-passivedefer": "Passive defer",
        "abusefilter-action-activedefer": "Active defer",
+       "abusefilter-action-deferblock": "Deferred block",
        "abusefilter-actionsummary-passivedefer": 
"[[Special:AbuseFilter/$1|Abuse Filter $1]]: $2",
        "abusefilter-actionsummary-activedefer": 
"[[Special:AbuseFilter/$1|Abuse Filter $1]]: $2",
+       "abusefilter-actionsummary-deferblock": "[[Special:AbuseFilter/$1|Abuse 
Filter $1]]: $2",
        "notification-header-flaggedrevs-deferred": "Your edit on $1 has been 
deferred for review.",
        "notification-header-flaggedrevs-deferred-auto": "Your edit on $1 has 
been automatically deferred for review.",
        "flaggedrevs-defer-help": "Help:Deferred changes",
        "notification-link-text-flaggedrevs-deferred-help": "Help",
-       "notification-link-text-flaggedrevs-deferred-log": "View log"
+       "notification-link-text-flaggedrevs-deferred-log": "View log",
+       "flaggedrevs-deferred-blocked": "Edits by this user have been deferred 
for review",
+       "logentry-block-deferblock": "$1 temporarily deferred changes 
{{GENDER:$2|blocked}} {{GENDER:$4|$3}}",
+       "logentry-block-deferunblock": "$1 deferred changes 
{{GENDER:$2|unblocked}} {{GENDER:$4|$3}}",
+       "flaggedrevs-deferunblock": "Use the form below to remove deferred 
changes blocks from an IP address or username."
 }
diff --git a/i18n/flaggedrevs/qqq.json b/i18n/flaggedrevs/qqq.json
index a55fb92..db89359 100644
--- a/i18n/flaggedrevs/qqq.json
+++ b/i18n/flaggedrevs/qqq.json
@@ -260,13 +260,20 @@
        "flaggedrevs-defer-notlatestauthor": "{{Flagged Revs}}\nError message 
when attempting to defer edits.",
        "abusefilter-edit-action-passivedefer": "{Flagged 
Revs}}\n{{doc-abusefilter-action}}\nAbuseFilter action to defer passively",
        "abusefilter-edit-action-activedefer": "{Flagged 
Revs}}\n{{doc-abusefilter-action}}\nAbuseFilter action to defer actively",
+       "abusefilter-edit-action-deferblock": "{Flagged 
Revs}}\n{{doc-abusefilter-action}}\nAbuseFilter action to temporarily actively 
defer all edits by the user",
        "abusefilter-action-passivedefer": "{Flagged 
Revs}}\n{{doc-abusefilter-action}}\nAbuseFilter action to defer passively",
        "abusefilter-action-activedefer": "{Flagged 
Revs}}\n{{doc-abusefilter-action}}\nAbuseFilter action to defer actively",
+       "abusefilter-action-deferblock": "{Flagged 
Revs}}\n{{doc-abusefilter-action}}\nAbuseFilter action to temporarily actively 
defer all edits by the user",
        "abusefilter-actionsummary-passivedefer": "{Flagged 
Revs}}\n{{doc-abusefilter-action}}\nComment left in defer log when an abuse 
filter passively defers an edit",
        "abusefilter-actionsummary-activedefer": "{Flagged 
Revs}}\n{{doc-abusefilter-action}}\nComment left in defer log when an abuse 
filter actively defers an edit",
+       "abusefilter-actionsummary-deferblock": "{Flagged 
Revs}}\n{{doc-abusefilter-action}}\nComment left in defer log when the abuse 
filter deferred changes block a user",
        "notification-header-flaggedrevs-deferred": "{Flagged Revs}}\nEcho 
notification for a defer action\n\nParameters:\n* $1 - page where deferral 
occured",
        "notification-header-flaggedrevs-deferred-auto": "{Flagged Revs}}\nEcho 
notification for an automatic defer action\n\nParameters:\n* $1 - page where 
deferral occured",
        "flaggedrevs-defer-help": "{Flagged Revs}}\nHelp page for deferred 
changes",
        "notification-link-text-flaggedrevs-deferred-help": "{Flagged 
Revs}}\nText for link to help page for deferred changes in echo notification",
-       "notification-link-text-flaggedrevs-deferred-log": "{Flagged 
Revs}}\nText for link to defer log in echo notification"
+       "notification-link-text-flaggedrevs-deferred-log": "{Flagged 
Revs}}\nText for link to defer log in echo notification",
+       "flaggedrevs-deferred-block": "{Flagged Revs}}\nLog summary for the 
defer of a user affected by a deferred changes block",
+       "logentry-block-deferblock": "{Flagged Revs}}\nLog summary for a 
deferred changes block",
+       "logentry-block-deferunblock": "{Flagged Revs}}\nLog summary for a 
deferred changes unblock",
+       "flaggedrevs-deferunblock": "{Flagged Revs}}\nExplanation at 
Special:DeferUnblock"
 }

-- 
To view, visit https://gerrit.wikimedia.org/r/317324
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I22a51065191c9c863f33f5116b6aa948c43e91d1
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/FlaggedRevs
Gerrit-Branch: master
Gerrit-Owner: Cenarium <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to