Jack Phoenix has uploaded a new change for review.
https://gerrit.wikimedia.org/r/325151
Change subject: [WIP] Version 1.4: Special:RegexBlock is now more of a
Special:Block copy
......................................................................
[WIP] Version 1.4: Special:RegexBlock is now more of a Special:Block copy
Lots and lots of code was copypasted (and modified) from core
/includes/specials/SpecialBlock.php.
Because core Special:Block supports range blocks, there is theoretical
readiness for implementing range blocks in RegexBlock, but:
* right now at least IPv6 ranges will fail the RegexBlockData::isValidRegex()
test in RegexBlockForm::validateTargetField()
* even if they wouldn't and ranges would be stored in the blockedby table,
there's no handling of ranges in RegexBlock::check() yet
Other bugfixes included here:
* Fixed stupid bug with RegexBlockData::isValidRegex() -- unlike what the name
suggests, the function was checking if the supplied regex is *invalid*
* Renamed RegexBlock::clearExpired() to the more accurate name removeBlock()
* Fixed IPv6 bug in RegexBlock::check() where IPv6 addresses weren't being
blocked correctly
Bug: T152260
Bug: T152262
Bug: T152166
Bug: T152177
Bug: T152179
Change-Id: Ic76123ffb06a3aceab90a79f4c4ede7f1640bfaa
---
M RegexBlockCore.php
M RegexBlockData.php
D RegexBlockUITemplate.php
M SpecialRegexBlock.php
M extension.json
M i18n/en.json
M regexblock.css
7 files changed, 622 insertions(+), 256 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/RegexBlock
refs/changes/51/325151/1
diff --git a/RegexBlockCore.php b/RegexBlockCore.php
index e9ce7cc..fe8efb3 100644
--- a/RegexBlockCore.php
+++ b/RegexBlockCore.php
@@ -32,7 +32,11 @@
return true;
}
- $ip_to_check = $wgRequest->getIP();
+ // sanitizeIP() check is needed for IPv6 -- upon saving a
RegexBlock,
+ // IPv6 IPs like ::1 (localhost) are expanded to
0:0:0:0:0:0:0:1, but
+ // $wgRequest->getIP() contains just "::1" so the checks fail
and
+ // blocked IPv6 IPs would still be able to edit
+ $ip_to_check = IP::sanitizeIP( $wgRequest->getIP() );
/* First check cache */
$blocked = self::isBlockedCheck( $current_user, $ip_to_check );
@@ -244,7 +248,7 @@
$names = array( 'ips' => '', 'exact' => '',
'regex' => '' );
while ( $row = $res->fetchObject() ) {
$key = 'regex';
- if ( $user->isIP( $row->blckby_name )
!= 0 ) {
+ if ( User::isIP( $row->blckby_name ) !=
0 ) {
$key = 'ips';
} elseif ( $row->blckby_exact != 0 ) {
$key = 'exact';
@@ -365,7 +369,7 @@
return $ret;
} else {
/* clean up an obsolete block */
- self::clearExpired( $single,
$blocked->blckby_blocker );
+ self::removeBlock( $single,
$blocked->blckby_blocker );
}
}
}
@@ -404,25 +408,26 @@
}
/**
- * Clean up an existing expired block
+ * Remove a block from the blockedby DB table.
*
- * @param string $username Name of the user
- * @param string $blocker Name of the blocker
+ * @param string $regex Username or regular expression to unblock
+ * @param string $blocker Name of the blocker [unused - remove?]
+ * @return bool True if unblocked succeeded, otherwise false
*/
- function clearExpired( $username, $blocker ) {
+ public static function removeBlock( $regex, $blocker ) {
$result = false;
$dbw = self::getDB( DB_MASTER );
$dbw->delete(
'blockedby',
- array( 'blckby_name' => $username ),
+ array( 'blckby_name' => $regex ),
__METHOD__
);
if ( $dbw->affectedRows() ) {
/* success, remember to delete cache key */
- self::unsetKeys( $username );
+ self::unsetKeys( $regex );
$result = true;
}
@@ -469,10 +474,10 @@
/**
* The actual blocking goes here, for each blocker
*
- * @param string $blocker
+ * @param string $blocker User name of the person who placed the block
* @param array $blocker_block_data
- * @param User $user
- * @param string $user_ip
+ * @param User $user User who is being blocked
+ * @param string $user_ip IP address of the user who is being blocked
*/
function blocked( $blocker, $blocker_block_data, $user, $user_ip ) {
if ( $blocker_block_data == null ) {
@@ -684,5 +689,4 @@
$updater->addExtensionUpdate( array( 'addTable',
'stats_blockedby', $file, true ) );
return true;
}
-
}
\ No newline at end of file
diff --git a/RegexBlockData.php b/RegexBlockData.php
index 5b28d70..3212d18 100644
--- a/RegexBlockData.php
+++ b/RegexBlockData.php
@@ -280,6 +280,6 @@
* @return bool
*/
public static function isValidRegex( $text ) {
- return ( sprintf( "%s", @preg_match( "/{$text}/", 'regex' ) )
=== '' );
+ return ( sprintf( '%s', @preg_match( "/{$text}/", 'regex' ) )
!== '' );
}
}
\ No newline at end of file
diff --git a/RegexBlockUITemplate.php b/RegexBlockUITemplate.php
deleted file mode 100644
index 92d66db..0000000
--- a/RegexBlockUITemplate.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup Templates
- */
-if ( !defined( 'MEDIAWIKI' ) ) {
- die( 1 );
-}
-
-/**
- * HTML template for Special:RegexBlock form
- * @ingroup Templates
- */
-class RegexBlockUITemplate extends QuickTemplate {
- function execute() {
- $checkExact = htmlspecialchars( (
$this->data['mRegexBlockedExact'] ) ) ? ' checked="checked"' : '';
- $checkCreation = htmlspecialchars( (
$this->data['mRegexBlockedCreation'] ) ) ? ' checked="checked"' : '';
-
- $msg = '';
- if ( $this->data['err'] != '' ) {
- $this->data['out']->setSubtitle( $this->msg(
'formerror' ) );
- $msg = '<h2 class="errorbox">' . $this->data['err'] .
'</h2>';
- } elseif ( $this->data['msg'] != '' ) {
- $msg = '<h2 class="successbox">' . $this->data['msg'] .
'</h2>';
- }
-?><div class="regexblock-msg"><?php echo $msg ?></div>
- <div class="regexblock-help"><?php echo wfMessage( 'regexblock-help'
)->parse() ?></div>
- <fieldset class="regexblock-fieldset" align="center">
- <legend><?php echo wfMessage( 'regexblock-form-submit'
)->escaped() ?></legend>
- <form name="regexblock" method="post" action="<?php echo
$this->data['action'] ?>">
- <table>
- <tr>
- <td align="right"><?php echo wfMessage(
'regexblock-form-username' )->escaped() ?></td>
- <td align="left">
- <input tabindex="1"
name="wpRegexBlockedAddress" id="wpRegexBlockedAddress"
class="mw-autocomplete-user" size="40" value="<?php echo
$this->data['regexBlockAddress'] ?>" />
- </td>
- </tr>
- <tr>
- <td align="right"><?php echo wfMessage(
'regexblock-form-reason' )->escaped() ?></td>
- <td align="left">
- <input tabindex="2"
name="wpRegexBlockedReason" id="wpRegexBlockedReason" size="40" value="<?php
echo $this->data['class']->mRegexBlockedReason ?>" />
- </td>
- </tr>
- <tr>
- <td align="right"><?php echo wfMessage(
'regexblock-form-expiry' )->escaped() ?></td>
- <td align="left">
- <select name="wpRegexBlockedExpire"
id="wpRegexBlockedExpire" tabindex="3">
- <?php
- foreach ( $this->data['expiries'] as $k => $v )
{
- $selected = htmlspecialchars( ( $k ==
$this->data['class']->mRegexBlockedExpire ) ) ? ' selected="selected"' : '';
- ?>
- <option value="<?php echo
htmlspecialchars( $v ) ?>"<?php echo $selected ?>><?php echo htmlspecialchars(
$v ) ?></option>
- <?php
- }
- ?>
- </select>
- </td>
- </tr>
- <tr>
- <td align="right"> </td>
- <td align="left">
- <input type="checkbox" tabindex="4"
name="wpRegexBlockedExact" id="wpRegexBlockedExact" value="1"<?php echo
$checkExact ?> />
- <label for="wpRegexBlockedExact"><?php
echo wfMessage( 'regexblock-form-match' )->escaped() ?></label>
- </td>
- </tr>
- <tr>
- <td align="right"> </td>
- <td align="left">
- <input type="checkbox" tabindex="5"
name="wpRegexBlockedCreation" id="wpRegexBlockedCreation" value="1"<?php echo
$checkCreation ?> />
- <label
for="wpRegexBlockedCreation"><?php echo wfMessage(
'regexblock-form-account-block' )->escaped() ?></label>
- </td>
- </tr>
- <tr>
- <td align="right"> </td>
- <td align="left">
- <input tabindex="6"
name="wpRegexBlockedSubmit" type="submit" value="<?php echo wfMessage(
'regexblock-form-submit' )->escaped() ?>" />
- </td>
- </tr>
- </table>
- <input type="hidden" name="wpEditToken" value="<?php echo
$this->data['token'] ?>" />
- </form>
- </fieldset>
- <br />
-<?php
- }
-}
\ No newline at end of file
diff --git a/SpecialRegexBlock.php b/SpecialRegexBlock.php
index f526cdd..83854e3 100644
--- a/SpecialRegexBlock.php
+++ b/SpecialRegexBlock.php
@@ -14,16 +14,33 @@
* @author Jack Phoenix <[email protected]>
* @copyright Copyright © 2007, Wikia Inc.
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
2.0 or later
+ * @note This file heavily reuses GPL-licensed code from MediaWiki core special
+ * page Special:Block (/includes/specials/SpecialBlock.php).
*/
-class RegexBlockForm extends SpecialPage {
+class RegexBlockForm extends FormSpecialPage {
public $numResults = 0;
public $numStatResults = 0;
public $mAction;
public $mFilter, $mRegexFilter;
public $mLimit;
public $mOffset;
- public $mError, $mMsg;
+
+ /** @var User|string|null User to be blocked, as passed either by
parameter (url?wpTarget=Foo)
+ * or as subpage (Special:Block/Foo) */
+ protected $target;
+
+ /** @var int Block::TYPE_ constant */
+ protected $type;
+
+ /** @var User|string The previous block target */
+ protected $previousTarget;
+
+ /** @var bool */
+ protected $alreadyBlocked;
+
+ /** @var array */
+ protected $preErrors = [];
/**
* Constructor -- set up the new, restricted special page
@@ -31,7 +48,6 @@
public function __construct() {
$this->mAction = '';
$this->mFilter = $this->mRegexFilter = '';
- $this->mError = $this->mMsg = '';
parent::__construct( 'RegexBlock', 'regexblock' );
}
@@ -52,30 +68,25 @@
/**
* Show the special page
*
- * @param string|null $subpage Parameter passed to the page, if any
+ * @param string|null $par Parameter passed to the page, if any
*/
- public function execute( $subpage ) {
+ public function execute( $par ) {
$out = $this->getOutput();
$request = $this->getRequest();
$user = $this->getUser();
- # If the user doesn't have the required 'regexblock'
permission, display an error
- if ( !$user->isAllowed( 'regexblock' ) ) {
- throw new PermissionsError( 'regexblock' );
- }
+ $this->setParameter( $par );
+ $this->setHeaders();
- # Show a message if the database is in read-only mode
- $this->checkReadOnly();
-
- # If user is blocked, s/he doesn't need to access this page
- if ( $user->isBlocked() ) {
- throw new UserBlockedError( $user->getBlock() );
- }
+ // This will throw exceptions if there's a problem
+ $this->checkExecutePermissions( $user );
// Initial output
$this->mTitle = $this->getPageTitle();
- $this->setHeaders();
+ // Our custom CSS
+ $out->addModuleStyles( 'ext.regexBlock.styles' );
+
$out->setPageTitle( $this->msg( 'regexblock-page-title' ) );
$this->mAction = $request->getVal( 'action' );
@@ -84,36 +95,22 @@
list( $this->mLimit, $this->mOffset ) =
$request->getLimitOffset();
- $this->mRegexBlockedAddress = $this->mRegexBlockedExact =
$this->mRegexBlockedCreation = $this->mRegexBlockedExpire =
$this->mRegexBlockedReason = '';
- if ( $this->mAction == 'submit' ) {
- $this->mRegexBlockedAddress = htmlspecialchars(
$request->getVal( 'wpRegexBlockedAddress', $request->getVal( 'ip' ) ) );
- $this->mRegexBlockedExact = $request->getInt(
'wpRegexBlockedExact' );
- $this->mRegexBlockedCreation = $request->getInt(
'wpRegexBlockedCreation' );
- $this->mRegexBlockedExpire = htmlspecialchars(
$request->getVal( 'wpRegexBlockedExpire' ) );
- $this->mRegexBlockedReason = htmlspecialchars(
$request->getVal( 'wpRegexBlockedReason' ) );
- }
-
/* Actions */
switch ( $this->mAction ) {
case 'success_block':
- $out->setSubTitle( $this->msg(
'regexblock-block-success' ) );
- $this->mMsg = $this->msg(
'regexblock-block-log', $request->getVal( 'ip' ) )->parse();
+ $out->setSubtitle( $this->msg(
'regexblock-block-success' ) );
+ $out->wrapWikiMsg( '<div
class="successbox">$1</div><br />' . "\n", [ 'regexblock-block-log', urldecode(
$request->getVal( 'ip' ) ) ] );
break;
case 'success_unblock':
- $out->setSubTitle( $this->msg(
'regexblock-unblock-success' ) );
- $this->mMsg = $this->msg(
'regexblock-unblock-log', $request->getVal( 'ip' ) )->parse();
+ $out->setSubtitle( $this->msg(
'regexblock-unblock-success' ) );
+ $out->wrapWikiMsg( '<div
class="successbox">$1</div><br />' . "\n", [ 'regexblock-unblock-log',
urldecode( $request->getVal( 'ip' ) ) ] );
break;
case 'failure_unblock':
- $this->mError = $this->msg(
'regexblock-unblock-error', $request->getVal( 'ip' ) )->text();
+ $out->wrapWikiMsg( '<div
class="errorbox">$1</div><br />' . "\n", [ 'regexblock-unblock-error',
urldecode( $request->getVal( 'ip' ) ) ] );
break;
case 'stats':
$blckid = $request->getVal( 'blckid' );
$this->showStatsList( $blckid );
- break;
- case 'submit':
- if ( $request->wasPosted() &&
$user->matchEditToken( $request->getVal( 'wpEditToken' ) ) ) {
- $this->mAction = $this->doSubmit();
- }
break;
case 'delete':
$this->deleteFromRegexBlockList();
@@ -121,44 +118,12 @@
}
if ( !in_array( $this->mAction, array( 'submit', 'stats' ) ) ) {
- $this->showForm();
- unset( $this->mError );
- unset( $this->mMsg );
+ $form = $this->getForm();
+ if ( $form->show() ) {
+ $this->onSuccess();
+ }
$this->showRegexList();
}
- }
-
- /**
- * Show the form for blocking IPs / users
- */
- private function showForm() {
- $out = $this->getOutput();
- $request = $this->getRequest();
-
- $token = htmlspecialchars( $this->getUser()->getEditToken() );
- $action = htmlspecialchars( $this->mTitle->getLocalURL( array(
'action' => 'submit' ) + $this->makeListUrlParams() ), ENT_QUOTES );
-
- $expiries = SpecialBlock::getSuggestedDurations(); //
RegexBlockData::getExpireValues();
- $regexBlockAddress = ( empty( $this->mRegexBlockedAddress ) &&
( $request->getVal( 'ip' ) != null ) &&
- ( $request->getVal( 'action' ) == null ) ) ?
$request->getVal( 'ip' ) : $this->mRegexBlockedAddress;
-
- $tpl = new RegexBlockUITemplate;
- $tpl->setRef( 'class', $this );
- $tpl->setRef( 'out', $out );
- $tpl->set( 'msg', $this->mMsg );
- $tpl->set( 'action', $action );
- $tpl->set( 'regexBlockAddress', $regexBlockAddress );
- $tpl->set( 'mRegexBlockedExact', $this->mRegexBlockedExact );
- $tpl->set( 'mRegexBlockedCreation',
$this->mRegexBlockedCreation );
- $tpl->set( 'err', $this->mError );
- $tpl->set( 'expiries', $expiries );
- $tpl->set( 'token', $token );
-
- // CSS & JS
- $out->addModuleStyles( 'ext.regexBlock.styles' );
- $out->addModules( 'mediawiki.userSuggest' );
-
- $out->addTemplate( $tpl );
}
/**
@@ -167,7 +132,7 @@
private function showRegexList() {
$out = $this->getOutput();
- $action = htmlspecialchars( $this->mTitle->getLocalURL(
$this->makeListUrlParams() ), ENT_QUOTES );
+ $action = htmlspecialchars( $this->getPageTitle()->getFullURL(
$this->makeListUrlParams() ), ENT_QUOTES );
$regexData = new RegexBlockData();
$lang = $this->getLanguage();
@@ -197,7 +162,8 @@
<form name="regexlist" method="get" action="' . $action
. '">
' . $this->msg( 'regexblock-view-blocked'
)->text() . '
<select name="filter">
- <option value="">' . $this->msg(
'regexblock-view-all' )->text() . '</option>' );
+ <option value="">' . $this->msg(
'regexblock-view-all' )->text() . '</option>'
+ );
if ( is_array( $blockers ) ) {
foreach ( $blockers as $id => $blocker ) {
@@ -207,19 +173,22 @@
}
$out->addHTML(
- '</select> ' . $this->msg(
'regexblock-regex-filter' )->text() . $this->msg( 'word-separator' )->text() . '
- <input type="text" name="rfilter"
id="regex_filter" value="' . $this->mRegexFilter . '" />
- <input type="submit" value="' . $this->msg(
'regexblock-view-go' )->text() . '" />
+ '</select> ' . $this->msg(
'regexblock-regex-filter' )->text() . $this->msg( 'word-separator' )->text() .
+ Html::hidden( 'title', $this->getPageTitle() ) .
+ Html::input( 'rfilter', $this->mRegexFilter,
'text', [ 'id' => 'regex_filter' ] ) .
+ '<input type="submit" value="' . $this->msg(
'regexblock-view-go' )->text() . '" />
</form>
<br />
- <form name="regexbyid" method="get" action="' . $action
. '">
- <input type="hidden" name="action"
value="stats" />' .
+ <form name="regexbyid" method="get" action="' . $action
. '">' .
+ Html::hidden( 'title', $this->getPageTitle() ) .
+ Html::hidden( 'action', 'stats' ) .
$this->msg( 'regexblock-view-block-id'
)->text() .
$this->msg( 'word-separator' )->text() .
'<input type="text" name="blckid" id="blckid"
value="" />
<input type="submit" value="' . $this->msg(
'regexblock-view-go' )->text() . '" />
</form>'
);
+
if ( !empty( $blockers ) ) {
$out->addHTML( '<ul id="regexblock_blocks">' );
$loop = 0;
@@ -241,7 +210,7 @@
$reason = '<i>' . $row['reason'] . '</i>';
$stats_link = Linker::linkKnown(
$this->mTitle,
- $this->msg( 'regexblock-view-stats' ),
+ $this->msg( 'regexblock-view-stats'
)->text(),
array(),
array( 'action' => 'stats', 'blckid' =>
$row['blckid'] )
);
@@ -259,8 +228,8 @@
$out->addHTML(
'<li>
- <b><span class="regexblock-target">' .
$row['blckby_name'] . '</span>' . $comma . $exact_match . $space .
$create_block . '</b>' . $comma . '
- (' . $this->msg(
'regexblock-view-block-by' ) . ' <b>' . $row['blocker'] . '</b>, ' . $reason .
') ' .
+ <code class="regexblock-target">' .
$row['blckby_name'] . '</code><b>' . $comma . $exact_match . $space .
$create_block . '</b>' . $comma . '
+ (' . $this->msg(
'regexblock-view-block-by' )->text() . ' <b>' . $row['blocker'] . '</b>, ' .
$reason . ') ' .
$this->msg( 'regexblock-view-time',
$row['datim'], $row['date'], $row['time'] )->text() . $comma .
'(' . $unblock_link . ') ' . $comma .
$row['expiry'] . $comma . ' (' . $stats_link . ')
</li>'
@@ -289,66 +258,16 @@
return $pieces;
}
- /* On submit */
- private function doSubmit() {
- /* empty name */
- if ( strlen( $this->mRegexBlockedAddress ) == 0 ) {
- $this->mError = $this->msg(
'regexblock-form-submit-empty' )->text();
- return false;
- }
-
- /* castrate regexes */
- if ( RegexBlockData::isValidRegex( $this->mRegexBlockedAddress
) ) {
- $this->mError = $this->msg(
'regexblock-form-submit-regex' )->text();
- return false;
- }
-
- /* check expiry */
- if ( strlen( $this->mRegexBlockedExpire ) == 0 ) {
- $this->mError = $this->msg(
'regexblock-form-submit-expiry' )->text();
- return false;
- }
-
- if ( $this->mRegexBlockedExpire != 'infinite' ) {
- $expiry = strtotime( $this->mRegexBlockedExpire );
- if ( $expiry < 0 || $expiry === false ) {
- $this->mError = $this->msg(
'ipb_expiry_invalid' )->text();
- return false;
- }
- $expiry = wfTimestamp( TS_MW, $expiry );
- } else {
- $expiry = $this->mRegexBlockedExpire;
- }
-
- $result = RegexBlockData::blockUser(
- $this->mRegexBlockedAddress,
- $expiry,
- $this->mRegexBlockedExact,
- $this->mRegexBlockedCreation,
- $this->mRegexBlockedReason
- );
- /* clear memcached */
- RegexBlock::unsetKeys( $this->mRegexBlockedAddress );
-
- /* redirect */
- $this->getOutput()->redirect( $this->mTitle->getFullURL( array(
- 'action' => 'success_block',
- 'ip' => $this->mRegexBlockedAddress
- ) + $this->makeListUrlParams() ) );
-
- return;
- }
-
/**
* Remove name or address from list - without confirmation
*/
private function deleteFromRegexBlockList() {
$request = $this->getRequest();
- $ip = $request->getVal( 'ip' );
+ $ip = urldecode( $request->getVal( 'ip' ) );
$blocker = $request->getVal( 'blocker' );
- $result = RegexBlock::clearExpired( $ip, $blocker );
+ $result = RegexBlock::removeBlock( $ip, $blocker );
if ( $result === true ) {
$this->getOutput()->redirect(
$this->mTitle->getFullURL( array(
@@ -442,4 +361,553 @@
$out->addWikiMsg( 'regexblock-nodata-found' );
}
}
+
+ /**
+ * Handle some magic here
+ *
+ * @param string $par
+ */
+ protected function setParameter( $par ) {
+ # Extract variables from the request. Try not to get into a
situation where we
+ # need to extract *every* variable from the form just for
processing here, but
+ # there are legitimate uses for some variables
+ $request = $this->getRequest();
+ list( $this->target, $this->type ) = self::getTargetAndType(
$par, $request );
+ 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 );
+ }
+
+ list( $this->previousTarget, /*...*/ ) =
+ self::parseTarget( $request->getVal( 'wpPreviousTarget'
) );
+ }
+
+ /**
+ * Customizes the HTMLForm a bit
+ *
+ * @param HTMLForm $form
+ */
+ protected function alterForm( HTMLForm $form ) {
+ $form->setWrapperLegendMsg( 'blockip-legend' );
+ $form->setHeaderText( '' );
+ $form->setSubmitDestructive();
+
+ $form->setSubmitTextMsg( 'regexblock-form-submit' );
+
+ $this->addHelpLink( 'Help:Blocking users' );
+
+ # Don't need to do anything if the form has been posted
+ if ( !$this->getRequest()->wasPosted() && $this->preErrors ) {
+ $s = $form->formatErrors( $this->preErrors );
+ if ( $s ) {
+ $form->addHeaderText( Html::rawElement(
+ 'div',
+ [ 'class' => 'error' ],
+ $s
+ ) );
+ }
+ }
+ }
+
+ /**
+ * Get the HTMLForm descriptor array for the block form
+ * @return array
+ */
+ protected function getFormFields() {
+ $suggestedDurations = self::getSuggestedDurations();
+
+ $a = [
+ 'Target' => [ // @note Formerly called
wpRegexBlockedAddress
+ 'type' => 'text',
+ 'label-message' => 'regexblock-form-username',
// 'ipaddressorusername',
+ 'id' => 'mw-bi-target',
+ 'size' => '45',
+ 'autofocus' => true,
+ 'required' => true,
+ 'validation-callback' => [ __CLASS__,
'validateTargetField' ],
+ 'cssclass' => 'mw-autocomplete-user', // used
by mediawiki.userSuggest
+ ],
+ 'Expiry' => [
+ 'type' => !count( $suggestedDurations ) ?
'text' : 'selectorother',
+ 'label-message' => 'ipbexpiry',
+ 'required' => true,
+ 'options' => $suggestedDurations,
+ 'other' => $this->msg( 'ipbother' )->text(),
+ 'default' => $this->msg( 'ipb-default-expiry'
)->inContentLanguage()->text(),
+ ],
+ 'Reason' => [
+ 'type' => 'selectandother',
+ 'maxlength' => 255,
+ 'label-message' => 'ipbreason',
+ 'options-message' => 'ipbreason-dropdown',
+ ],
+ /*'CreateAccount'*/'RegexBlockedCreation' => [
+ 'type' => 'check',
+ 'label-message' =>
'regexblock-form-account-block', //'ipbcreateaccount',
+ 'default' => true,
+ ],
+ 'RegexBlockedExact' => [
+ 'type' => 'check',
+ 'label-message' => 'regexblock-form-match',
+ 'default' => false
+ ]
+ ];
+
+ # This is basically a copy of the Target field, but the user
can't change it, so we
+ # can see if the warnings we maybe showed to the user before
still apply
+ $a['PreviousTarget'] = [
+ 'type' => 'hidden',
+ 'default' => false,
+ ];
+
+ $this->maybeAlterFormDefaults( $a );
+
+ return $a;
+ }
+
+ /**
+ * If the user has already been blocked with similar settings, load
that block
+ * and change the defaults for the form fields to match the existing
settings.
+ * @param array $fields HTMLForm descriptor array
+ * @return bool Whether fields were altered (that is, whether the
target is
+ * already blocked)
+ */
+ protected function maybeAlterFormDefaults( &$fields ) {
+ # This will be overwritten by request data
+ $fields['Target']['default'] = (string)$this->target;
+
+ if ( $this->target ) {
+ if ( !RegexBlockData::isValidRegex(
(string)$this->target ) ) {
+ $this->preErrors = array_merge(
$this->preErrors, [ 'regexblock-form-submit-regex' ] );
+ }
+ /*
+ $status = SpecialBlock::validateTarget( $this->target,
$this->getUser() );
+ if ( !$status->isOK() ) {
+ $errors = $status->getErrorsArray();
+ $this->preErrors = array_merge(
$this->preErrors, $errors );
+ }
+ */
+ }
+
+ # This won't be
+ $fields['PreviousTarget']['default'] = (string)$this->target;
+ }
+
+ /**
+ * Add header elements like help text, etc.
+ * @return string
+ */
+ protected function preText() {
+ $this->getOutput()->addModules( [ 'mediawiki.special.block',
'mediawiki.userSuggest' ] );
+
+ $blockCIDRLimit = $this->getConfig()->get( 'BlockCIDRLimit' );
+ // @note Originally used 'blockiptext'
+ // @todo FIXME: (eventually) use the passed params in the i18n
msg for realz
+ $text = $this->msg( 'regexblock-help', $blockCIDRLimit['IPv4'],
$blockCIDRLimit['IPv6'] )->parse();
+
+ return $text;
+ }
+
+ /**
+ * Add footer elements to the form
+ * @return string
+ */
+ protected function postText() {
+ $links = [];
+
+ $this->getOutput()->addModuleStyles( 'mediawiki.special' );
+
+ # Link to the user's contributions, if applicable
+ if ( $this->target instanceof User ) {
+ $contribsPage = SpecialPage::getTitleFor(
'Contributions', $this->target->getName() );
+ $links[] = Linker::link(
+ $contribsPage,
+ $this->msg( 'ipb-blocklist-contribs',
$this->target->getName() )->escaped()
+ );
+ }
+
+ $user = $this->getUser();
+
+ # Link to edit the block dropdown reasons, if applicable
+ if ( $user->isAllowed( 'editinterface' ) ) {
+ $links[] = Linker::linkKnown(
+ $this->msg( 'ipbreason-dropdown'
)->inContentLanguage()->getTitle(),
+ $this->msg( 'ipb-edit-dropdown' )->escaped(),
+ [],
+ [ 'action' => 'edit' ]
+ );
+ }
+
+ $text = Html::rawElement(
+ 'p',
+ [ 'class' => 'mw-ipb-conveniencelinks' ],
+ $this->getLanguage()->pipeList( $links )
+ );
+
+ $userTitle = self::getTargetUserTitle( $this->target );
+ if ( $userTitle ) {
+ # Get relevant extracts from the block and suppression
logs, if possible
+ $out = '';
+
+ LogEventsList::showLogExtract(
+ $out,
+ 'block',
+ $userTitle,
+ '',
+ [
+ 'lim' => 10,
+ 'msgKey' => [ 'blocklog-showlog',
$userTitle->getText() ],
+ 'showIfEmpty' => false
+ ]
+ );
+ $text .= $out;
+ }
+
+ return $text;
+ }
+
+ /**
+ * Get a user page target for things like logs.
+ * This handles account and IP range targets.
+ * @param User|string $target
+ * @return Title|null
+ */
+ protected static function getTargetUserTitle( $target ) {
+ if ( $target instanceof User ) {
+ return $target->getUserPage();
+ } elseif ( IP::isIPAddress( $target ) ) {
+ return Title::makeTitleSafe( NS_USER, $target );
+ }
+
+ return null;
+ }
+
+ /**
+ * Determine the target of the block, and the type of target
+ *
+ * @param string $par Subpage parameter passed to setup, or data value
from
+ * the HTMLForm
+ * @param WebRequest $request Optionally try and get data from a
request too
+ * @return array( User|string|null, Block::TYPE_ constant|null )
+ */
+ public static function getTargetAndType( $par, WebRequest $request =
null ) {
+ $i = 0;
+ $target = null;
+
+ while ( true ) {
+ switch ( $i++ ) {
+ case 0:
+ # The HTMLForm will check wpTarget
first and only if it doesn't get
+ # a value use the default, which will
be generated from the options
+ # below; so this has to have a higher
precedence here than $par, or
+ # we could end up with different values
in $this->target and the HTMLForm!
+ if ( $request instanceof WebRequest ) {
+ $target = $request->getText(
'wpTarget', null );
+ }
+ break;
+ case 1:
+ $target = $par;
+ break;
+ case 2:
+ if ( $request instanceof WebRequest ) {
+ $target = $request->getText(
'ip', null );
+ }
+ break;
+ case 3:
+ # B/C @since 1.18
+ if ( $request instanceof WebRequest ) {
+ $target = $request->getText(
'wpBlockAddress', null );
+ }
+ break;
+ case 4:
+ break 2;
+ }
+
+ list( $target, $type ) = self::parseTarget( $target );
+
+ if ( $type !== null ) {
+ return [ $target, $type ];
+ }
+ }
+
+ return [ null, null ];
+ }
+
+ /**
+ * <s>From an existing Block,</s> get the target and the type of target.
+ * Note that, except for null, it is always safe to treat the target
+ * as a string; for User objects this will return User::__toString()
+ * which in turn gives User::getName().
+ *
+ * Had to override this to take regexes into account, which
SpecialBlock's
+ * method obviously doesn't, because as of MW 1.28 core doesn't have
native
+ * support for blocking via regexes. One day...
+ *
+ * @param string|int|User|null $target
+ * @return array( User|String|null, Block::TYPE_ constant|null )
+ */
+ public static function parseTarget( $target ) {
+ # We may have been through this before
+ if ( $target instanceof User ) {
+ if ( IP::isValid( $target->getName() ) ) {
+ return [ $target, self::TYPE_IP ];
+ } else {
+ return [ $target, self::TYPE_USER ];
+ }
+ } elseif ( $target === null ) {
+ return [ null, null ];
+ }
+
+ $target = trim( $target );
+
+ if ( IP::isValid( $target ) ) {
+ # We can still create a User if it's an IP address, but
we need to turn
+ # off validation checking (which would exclude IP
addresses)
+ return [
+ User::newFromName( IP::sanitizeIP( $target ),
false ),
+ Block::TYPE_IP
+ ];
+
+ } elseif ( IP::isValidBlock( $target ) ) {
+ # Can't create a User from an IP range
+ return [ IP::sanitizeRange( $target ),
Block::TYPE_RANGE ];
+ }
+
+ # Consider the possibility that this is not a username at all
+ # but actually an old subpage (bug #29797)
+ if ( strpos( $target, '/' ) !== false ) {
+ # An old subpage, drill down to the user behind it
+ $target = explode( '/', $target )[0];
+ }
+
+ $userObj = User::newFromName( $target );
+ if ( $userObj instanceof User ) {
+ # Note that since numbers are valid usernames, a
$target of "12345" will be
+ # considered a User. If you want to pass a block ID,
prepend a hash "#12345",
+ # since hash characters are not valid in usernames or
titles generally.
+ return [ $userObj, Block::TYPE_USER ];
+ } elseif ( RegexBlockData::isValidRegex( $target ) ) {
+ return [ $target, 6 /* Block::TYPE_ constants are
numbered 1-5, so using 6 here is safe for now */ ];
+ } else {
+ # WTF?
+ return [ null, null ];
+ }
+ }
+
+ /**
+ * HTMLForm field validation callback for Target field.
+ *
+ * @param string $value User-supplied value to check for validity
+ * @param array $alldata
+ * @param HTMLForm $form
+ * @return Message
+ */
+ public static function validateTargetField( $value, $alldata, $form ) {
+ if ( RegexBlockData::isValidRegex( $value ) ) {
+ // valid regex is all it takes [[for now]]...
+ return true;
+ } else {
+ $errors = [];
+ $errors[0] = [ 'regexblock-form-submit-regex' ];
+ return call_user_func_array( [ $form, 'msg' ],
$errors[0] );
+ }
+ }
+
+ /**
+ * Given the form data, actually implement a block.<s>This is also
called from ApiBlock.</s>
+ *
+ * @param array $data
+ * @param IContextSource $context
+ * @return bool|string
+ */
+ public static function processForm( array $data, IContextSource
$context ) {
+ global $wgContLang;
+
+ $performer = $context->getUser();
+
+ // Handled by field validator callback
+ // self::validateTargetField( $data['Target'] );
+
+ /** @var User $target */
+ list( $target, $type ) = self::getTargetAndType(
$data['Target'] );
+ if ( $type == Block::TYPE_USER ) {
+ $user = $target;
+ $target = $user->getName();
+ $userId = $user->getId();
+
+ # Give admins a heads-up before they go and block
themselves. Much messier
+ # to do this for IPs, but it's pretty unlikely they'd
ever get the 'block'
+ # permission anyway, although the code does allow for
it.
+ # Note: Important to use $target instead of
$data['Target']
+ # since both $data['PreviousTarget'] and $target are
normalized
+ # but $data['target'] gets overridden by
(non-normalized) request variable
+ # from previous request.
+ if ( $target === $performer->getName() &&
+ ( $data['PreviousTarget'] !== $target )
+ ) {
+ return [ 'ipb-blockingself',
'ipb-confirmaction' ];
+ }
+ } elseif ( $type == Block::TYPE_RANGE ) {
+ $user = null;
+ $userId = 0;
+ } elseif ( $type == Block::TYPE_IP ) {
+ $user = null;
+ $target = $target->getName();
+ $userId = 0;
+ } elseif ( $type == 6 /* = our own identifier for regex-based
blocks */ ) {
+ // for RegexBlock assume that this case means that the
target is
+ // a regular expression, for which there is no
Block::TYPE_*
+ // constant in MW core, obviously...
+ $user = null;
+ $userId = 0;
+ } else {
+ # This should have been caught in the form field
validation
+ return [ 'badipaddress' ];
+ }
+
+ $expiryTime = SpecialBlock::parseExpiryInput( $data['Expiry'] );
+
+ if (
+ // an expiry time is needed
+ ( strlen( $data['Expiry'] ) == 0 ) ||
+ // can't be a larger string as 50 (it should be a time
format in any way)
+ ( strlen( $data['Expiry'] ) > 50 ) ||
+ // check, if the time could be parsed
+ !$expiryTime
+ ) {
+ return [ 'ipb_expiry_invalid' ];
+ }
+
+ // an expiry time should be in the future, not in the
+ // past (wouldn't make any sense) - bug T123069
+ if ( $expiryTime < wfTimestampNow() ) {
+ return [ 'ipb_expiry_old' ];
+ }
+
+ $result = RegexBlockData::blockUser(
+ $target,
+ $expiryTime,
+ $data['RegexBlockedExact'],
+ $data['RegexBlockedCreation'],
+ # Truncate reason for whole multibyte characters
+ $wgContLang->truncate( $data['Reason'][0], 255 )
+ );
+
+ // clear memcached
+ RegexBlock::unsetKeys( $target );
+
+/*
+ $logAction = 'block';
+
+ # Prepare log parameters
+ $logParams = [];
+ $logParams['5::duration'] = $data['Expiry'];
+ $logParams['6::flags'] = self::blockLogFlags( $data, $type );
+
+ # Make log entry
+ $logEntry = new ManualLogEntry( 'regexblock', $logAction );
+ $logEntry->setTarget( Title::makeTitle( NS_USER, $target ) );
+ $logEntry->setComment( $data['Reason'][0] );
+ $logEntry->setPerformer( $performer );
+ $logEntry->setParameters( $logParams );
+ # Relate log ID to block IDs (bug 25763)
+ $blockIds = array_merge( [ $status['id'] ], $status['autoIds']
);
+ $logEntry->setRelations( [ 'ipb_id' => $blockIds ] );
+ $logId = $logEntry->insert();
+ $logEntry->publish( $logId );
+*/
+ # Report to the user
+ return true;
+ }
+
+ /**
+ * Get an array of suggested block durations from MediaWiki:Ipboptions
+ *
+ * @param Language|null $lang The language to get the durations in, or
null to use
+ * the wiki's content language
+ * @return array
+ */
+ public static function getSuggestedDurations( $lang = null ) {
+ $a = [];
+ $msg = $lang === null
+ ? wfMessage( 'ipboptions' )->inContentLanguage()->text()
+ : wfMessage( 'ipboptions' )->inLanguage( $lang
)->text();
+
+ if ( $msg == '-' ) {
+ return [];
+ }
+
+ foreach ( explode( ',', $msg ) as $option ) {
+ if ( strpos( $option, ':' ) === false ) {
+ $option = "$option:$option";
+ }
+
+ list( $show, $value ) = explode( ':', $option );
+ $a[$show] = $value;
+ }
+
+ return $a;
+ }
+
+ /**
+ * Return a comma-delimited list of "flags" to be passed to the log
+ * reader for this block, to provide more information in the logs
+ * @param array $data From HTMLForm data
+ * @param int $type Block::TYPE_ constant (USER, RANGE, or IP)
+ * @return string
+ */
+ protected static function blockLogFlags( array $data, $type ) {
+ $flags = [];
+
+ if ( $data['RegexBlockedCreation'] ) {
+ // For grepping: message block-log-flags-nocreate
+ $flags[] = 'nocreate';
+ }
+
+ // For grepping: message block-log-flags-nousertalk
+ $flags[] = 'nousertalk';
+
+ return implode( ',', $flags );
+ }
+
+ /**
+ * Process the form on POST submission.
+ * @param array $data
+ * @param HTMLForm $form
+ * @return bool|array True for success, false for didn't-try, array of
errors on failure
+ */
+ public function onSubmit( array $data, HTMLForm $form = null ) {
+ return self::processForm( $data, $form->getContext() );
+ }
+
+ /**
+ * Do something exciting on successful processing of the form, most
likely to show a
+ * confirmation message
+ */
+ public function onSuccess() {
+ $out = $this->getOutput();
+ $this->getOutput()->redirect(
$this->getPageTitle()->getFullURL( array(
+ 'action' => 'success_block',
+ 'ip' => $this->target
+ ) + $this->makeListUrlParams() ) );
+ }
+
+ /**
+ * 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 );
+ }
}
\ No newline at end of file
diff --git a/extension.json b/extension.json
index f6feba2..4b2b43f 100644
--- a/extension.json
+++ b/extension.json
@@ -1,6 +1,6 @@
{
"name": "RegexBlock",
- "version": "1.3",
+ "version": "1.4",
"author": [
"Bartek Łapiński",
"Tomasz Klim",
@@ -31,8 +31,7 @@
"AutoloadClasses": {
"RegexBlock": "RegexBlockCore.php",
"RegexBlockData": "RegexBlockData.php",
- "RegexBlockForm": "SpecialRegexBlock.php",
- "RegexBlockUITemplate": "RegexBlockUITemplate.php"
+ "RegexBlockForm": "SpecialRegexBlock.php"
},
"Hooks": {
"ContributionsToolLinks": [
diff --git a/i18n/en.json b/i18n/en.json
index f4b5fba..b354cd7 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -22,7 +22,6 @@
"regexblock-reason-regex": "This username is prevented from editing due
to vandalism or other disruption by a user with a similar name.\nPlease create
an alternate user name or [[$1|contact us]] about the problem",
"regexblock-form-username": "IP address or username:",
"regexblock-form-reason": "Reason:",
- "regexblock-form-expiry": "Expiry:",
"regexblock-form-match": "Exact match",
"regexblock-form-account-block": "Block creation of new accounts",
"regexblock-form-submit": "Block this user",
diff --git a/regexblock.css b/regexblock.css
index 5477038..cea8ded 100644
--- a/regexblock.css
+++ b/regexblock.css
@@ -21,29 +21,11 @@
border: 1px solid #2F6FAB;
}
-.regexblock-fieldset input[name="wpRegexBlockedSubmit"] {
- background-color: #900;
- background-image: -moz-linear-gradient(top, #c00 20%, #670000 70%);
- background-image: -webkit-gradient(linear, 0% 20%, 0% 70%, from(#c00),
to(#670000));
- border-color: #ff0000;
- color: #FFF;
- box-shadow: 0 0 0 1px #800000;
- -moz-box-shadow: 0 0 0 1px #800000;
- -webkit-box-shadow: 0 1px 0 #800000, 0 -1px 0 #800000, 1px 0 0 #800000,
-1px 0 0 #800000;
-}
-
/* dashed blue line between rows */
#regexblock_blocks li,
#regexblock_triggers li {
border-bottom: 1px dashed #778899;
- font-size: 11px;
padding-bottom: 2px;
-}
-
-/* target name in green */
-#regexblock_blocks .regexblock-target {
- color: #3B7F07;
- font-size: 12px;
}
/* expired block time in red */
--
To view, visit https://gerrit.wikimedia.org/r/325151
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ic76123ffb06a3aceab90a79f4c4ede7f1640bfaa
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/RegexBlock
Gerrit-Branch: master
Gerrit-Owner: Jack Phoenix <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits