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