r45428 - Code Review

From MediaWiki.org

Jump to: navigation, search
Repository:MediaWiki
Revision:r45427 | r45428 (on ViewVC) | r45429 >
Date:00:30, 6 January 2009
Author:ashley
Status:deferred
Tags:
Comment:regexBlock updates:
*major code updates from wikia codebase (stats are now a part of SpecialRegexBlock.php etc.)
*coding style tweaks
*delete old readme files and update the actual README to point to mediawiki.org
*use normal version param to $wgExtensionCredits instead of svn-date & svn-revision
*add sql schema into svn
*no longer depends on SimplifiedRegex extension, is self-contained now
*delete SpecialRegexBlockStats.php, now useless
*add the right-regexblock message
Modified paths:

Diff [purge]

Index: trunk/extensions/regexBlock/RegexBlock.xml
===================================================================
--- trunk/extensions/regexBlock/RegexBlock.xml	(revision 45427)
+++ trunk/extensions/regexBlock/RegexBlock.xml	(revision 45428)
@@ -1,357 +0,0 @@
-<mediawiki lang="en" version="0.3" schemaLocation="http://www.mediawiki.org/xml/export-0.3/ http://www.mediawiki.org/xml/export-0.3.xsd" xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.mediawiki.org/xml/export-0.3/">
-  <siteinfo>
-    <sitename>Wikia</sitename>
-    <base>http://www.wikia.com/wiki/Main_Page
-    <generator>MediaWiki 1.7alpha</generator>
-    <case>first-letter</case>
-      <namespaces>
-      <namespace key="-2">Media</namespace>
-      <namespace key="-1">Special</namespace>
-      <namespace key="0"></namespace>
-      <namespace key="1">Talk</namespace>
-      <namespace key="2">User</namespace>
-      <namespace key="3">User talk</namespace>
-      <namespace key="4">Staff Wiki</namespace>
-      <namespace key="5">Staff Wiki talk</namespace>
-      <namespace key="6">Image</namespace>
-      <namespace key="7">Image talk</namespace>
-      <namespace key="8">MediaWiki</namespace>
-      <namespace key="9">MediaWiki talk</namespace>
-      <namespace key="10">Template</namespace>
-      <namespace key="11">Template talk</namespace>
-      <namespace key="12">Help</namespace>
-      <namespace key="13">Help talk</namespace>
-      <namespace key="14">Category</namespace>
-      <namespace key="15">Category talk</namespace>
-      <namespace key="100">Portal</namespace>
-      <namespace key="101">Portal talk</namespace>
-      <namespace key="102">Blog</namespace>
-    </namespaces>
-  </siteinfo>
-  <page>
-    <title>RegexBlock</title>
-    <id>1873</id>
-    <revision>
-      <id>6278</id>
-      <timestamp>2006-12-29T20:42:46Z</timestamp>
-      <contributor>
-        <username>Sannse</username>
-        <id>5</id>
-      </contributor>
-      <comment>/* Help page */</comment>
-      <text space="preserve">:&lt;small&gt;See
-[[Cross wiki user blocks]] for a list of users blocked the old
-way.&lt;/small&gt;
-&lt;H1&gt;Product Requirements&lt;/H1&gt;
-==Product Description==
-: This is the automated version of the original regexBlock extension.
-It takes data out of shared the database instead out of hardwritten
-text inside of the file (''uses memcached to speed up the operations,
-mostly on the side of checking edits'') and applies blocking to all
-users, groupped by blocker's names. There is also a Special Page
-(restricted to users with the preexisting 'block' ability) that allows
-to block, view (also view by blockers) and unblock user names and IP
-addresses. Current version also allows to block entire IP addresses (no
-range or partial blockings in this case).
-: Will also include in this one an ability to prevent new account
-creation (yes, such a thing already exists in IP Blocks, but is not
-multi-wikia-wide).
-==Product Assumptions==
-* we use a shared database (so the block is enabled on multiple wikias)
-* we use memcached (so database operations are sped up)
-* user of this special page should have the rights to block
-* we take care of account creation prevention multi-wikia style right
-here
-==Product Features==
-:;Basic functionality
-:: Ability to block users by their names when they try to edit pages,
-getting data out of the database, special page for managing blocks.
-:;Extended functionality
-:: Ability to block IP addresses, use of memcached, viewing blocks by
-blockers.
-:;Additional functionality
-:: Ability to block new account creation on multiple wikias, displaying
-efficiency statistics for particular blocks (who, when, from where
-tried to edit and was blocked), allowing to set expiration period for
-blocks (which is already done).
-==Key Product Concepts==
-:;Data fetched from shared database
-:: Data should be fetched from a shared database, supported with the
-use of memcached instead of hardwritten from the file.
-:;Submit form on one page with the list
-:: Easier to manage than having them on two separated special pages.
-:;May be extended by having more statistics, timestamps, allow blocks
-by IP ranges, and such
-:: We can add some statistics into separate tables (like the reason -
-rather an internal note for admin, why this particular user has been
-blocked, or the IP's from which the user tried to login etc.).
-==Product Unknowns==
-* Should we have a similar page for usernames excluded from blocking
-(when we block some IPs)? This could be linked to this project (the new
-special page would fetch some data from this one).
-* Include ability to block entire IP ranges? There are already
-indications that it would be useful.
-* Include timestamps? For now, the blocks are there for infinity.
-** Timestamps are now included. Expired blocks are deleted
-automatically when a blocked user tries to edit and an expired block is
-then applied. In such case, the block is discarded and user can edit.
-Admin can also remove expired blocks manually from list (they are
-denoted 'EXPIRED' in red font)
-* Show more information? Like for example statistics, which users tried
-to login from which IPs and how many times, etc.
-==Potential Product Issues==
-* As we are able to block entire IP addresses, so there is more need
-for a utility to allow specific users identified by their usernames to
-edit anyway, even when they log from the blocked address. Similar
-request already exists here:
-[[Product_Priorities:_Ops_Upgrades_Bugs#General]]. The special page to
-exclude blocks could integrate both IP Blocks and Regexblocks or it
-could be just two separate special pages.
-* &lt;strike&gt;The blocks are permanent at the moment. So there is
-more attention required to them.&lt;/strike&gt;
-: Which is untrue now. The blocks can be made with a chosen expiration
-time. After that, then can be removed manually or on block. Either way,
-they will be harmless.
-* also a note: on older versions of MW when the user is regexblocked
-(s)he cannot also create accounts (same goes for normal IP blocks), on
-newer (I've tested it personally on 1.8.2) it allows a separate user
-account creation block - it's about the
-code, and since I was developing a clean extension, I didn't want to
-alter the MW code itself. This leads to a situation, when the same
-block of one vandal will allow him/her to create account on one wiki,
-and disallow on the other (assuming that they will have different
-versions and the option 'block account creation' will be unchecked).
-==Product Interface==
-: Has one Special Page named Regexblock. It displays a form, used for
-adding blocks, and a list, used for managing blocks.
-: The form has a field, specyfing user name or IP that we want to
-block. While not required for a successful block, there are three more
-options: whether match should be '''exact''', whether we should deny
-this user '''account creation''', or the '''expiration period''', after
-which the block will be invalid.
-: The list can be filtered by usernames of the blockers. Each row
-displays following information: ''username or IP address blocked'',
-''username of the blocker'' and a ''link to unblock''. The unblock
-requires no confirmation and is processed instantenuosly. The list can
-be traversed as all standard lists in special pages (prev, next,
-display by 20, 50 etc.). Each row also has the link to '''statistics'''
-for a particular block.
-==Product Structure==
-* '''Files'''
-** '''regexBlock.php''' ''contains includes for the files in the
-folder, this one replaces old regexBlock.php
-** '''/regexBlock''' ''folder containg the core of the extension plus
-docs''
-*** '''README''' ''contains installation instructions and product
-description''
-*** '''regexBlockCore.php''' ''contains the blocking utility and use of
-the hook''
-*** '''SpecialRegexBlock.php''' ''Special Page for viewing and managing
-block list''
-*** '''SpecialRegexBlockStats.php''' ''Special Page for displaying
-stats for particular blocks''
-*** '' '''SimplifiedRegex.php''' - this one is required also by
-[[SpamRegex]] ''
-* '''SQL''' CREATE TABLE `blockedby` ( `blckby_id` int(5) NOT NULL
-auto_increment, `blckby_name` varchar(255) character set latin1 collate
-latin1_bin NOT NULL, `blckby_blocker` varchar(255) character set latin1
-collate latin1_bin NOT NULL, `blckby_timestamp` char(14) NOT NULL,
-`blckby_expire` char(14) NOT NULL, `blckby_create` tinyint(1) NOT NULL
-default '1', `blckby_exact` tinyint(1) NOT NULL default '0',
-`blckby_reason` tinyblob NOT NULL, PRIMARY KEY (`blckby_id`), UNIQUE
-KEY `blckby_name` (`blckby_name`), KEY `blckby_timestamp`
-(`blckby_timestamp`), KEY `blckby_expire` (`blckby_expire`) ) ; CREATE
-TABLE `stats_blockedby` ( `stats_id` int(8) NOT NULL auto_increment,
-`stats_user` varchar(255) character set latin1 collate latin1_bin NOT
-NULL, `stats_blocker` varchar(255) character set latin1 collate
-latin1_bin NOT NULL, `stats_timestamp` char(14) NOT NULL, `stats_ip`
-char(15) NOT NULL, `stats_match` varchar(255) character set latin1
-collate latin1_bin NOT NULL default '-', `stats_wiki` varchar(255) NOT
-NULL default '', PRIMARY KEY (`stats_id`), KEY `stats_timestamp`
-(`stats_timestamp`) ) ;
-* '''Installation'''
-# Create tables in the shared database.
-# Copy folder /regexBlock to /extensions, copy SimplifiedRegex.php to
-/extensions ('''do not''' copy regexBlock.php yet!).
-# Edit regexBlock.php and set '''REGEXBLOCK_PATH''' to its correct
-value.
-# Copy regexBlock.php to /extensions.
-:That should do the trick. If people belonging to staff cannot access
-the extension, check if relogin helps. If not, please check if the
-staff permissions are not '''overwritten''' after the initialization of
-the extension. If that is the case, the permissions should be rather
-appended (the extension adds one new permission right).
-==Product Use Cases==
-# '''Blocking a vandalising user by a username'''
-::A user is vandalising over different wikias. An admin blocks the user
-by adding his/her username to the block list. The user cannot edit
-pages anymore until the block is released.
-::;Step 1 (An admin blocks user)'''
-:::Admin inserts the username into the database via the special page
-regexblock.
-::;Step 2 (The user cannot edit pages)
-:::Blocked user (a user that his/her username '''contains''' the
-blocked username) tries to edit. There goes a block check, and there is
-a match. Now, the expiration check is done. If the block is still
-valid, unfortunate (vandalising) user will be displayed a message that
-this particular user is blocked, along with the name of the blocker.
-Also, on a successful block, an entry into the database is made.
-::;Step 3 (Admin releases the block)
-:::When the situation is under control, admin can ublock the user (or
-not, if the user's actions made clear that the block should be
-permanent). Admin can also view statistics for this particular block
-(frequent attempts to edit despite being blocked may encourage admin to
-make a more permanent block...).
-# '''Blocking a vandalising user by an IP address'''
-:: A user is vandalising over different wikias. An admin blocks the
-user by adding his/her IP address to the block list. No users logging
-from that IP will be able to edit the pages.
-::;Step 1 (An admin blocks '''full''' IP address)
-:::Admin inserts the '''full''' IP address into the database via the
-special page regexblock (''a partial or range IP address will be
-treated like a username. This is to avoid situations such as when a '3'
-block blocks all IP's including '3'. Regular expressions.''). ::;Step 2
-(The user cannot edit pages)
-:::When a user logging in from a particular IP address (it has to be an
-'''exact''' match) tries to edit, he/she will be displayed a message
-that this particular user is blocked, along with the name of the
-blocker. Also, all non-logged users from that IP address will be
-blocked.
-::;Step 3 (Admin releases the block)
-:::When the situation is under control, admin can ublock this IP
-address (or not, if the user's actions made clear that the block should
-be permanent).
-==Product Bugs==
-:Please test it on http://fp006.qa.wikia.com/wiki/Special:Regexblock.
-(it requires staff group rights). Account creation prevention mode may
-not work properly at the moment - still working on that.
-:'''Known Bugs''':
-:* there is a little glitch when someone unblocks the last block by a
-particular blocker (it shows 'All' on the select, but the list is
-empty, the only exception is when unblocked row was the last on the
-global list)
-==Discussion==
-[[User:Kaz3t]]: I'm not convinced that currently way for blocking user
-by name is the best. For example user name can consist of his real name
-and surname. So if we block user "John" we also block users "Johnny",
-"John Smith", etc. In my opinion we should have choice - multi-block
-(as it is now) and single-block (for example when we want to block this
-and only this specified user we put his name between quotation-marks).
-::: Yes, you got a point. Umm, but wouldn't a checkbox be better? It
-would be more obvious on the first sight, I believe.
---[[User:Bartek|Bartek]] 16:03, 17 November 2006 (UTC)''
-::: I added a checkbox just for that, should be working fine.
---[[User:Bartek|Bartek]] 16:01, 20 November 2006 (UTC)
-* The message that a user sees is the same whether it is a similar name
-block or a specific name block (or even an IP block). I think this
-needs to either be made more general, or made variable. The reason set
-is: "This username is prevented from editing due to vandalism by a user
-with a similar name. Please create an alternate user name or contact
-Wikia about the problem"
-:::: There are four types of messages now (displayed to the blocked
-user). First, is the reason specified by an admin. If there is none,
-different messages will be displayed for: exact username match, IP
-address match and a regex username match. - --[[User:Bartek|Bartek]]
-13:07, 24 November 2006 (UTC)
-* Is it possible to block ''just'' creation of accounts? for example to
-stop future creation of sannse-like accounts while still allowing me to
-edit? (this isn't vital though)
-:::: Good point. For the moment, it is not possible with this
-extension. It will be added in the future. - --[[User:Bartek|Bartek]]
-09:42, 24 November 2006 (UTC)
-* When it's an exact match block, this is mentioned on the listing.
-Could it also be
-mentioned when it is not exact? It would make this listing clearer. For
-example:
-** Bartek (exact match) (account creation block) (blocked by: Sannse)
-on (etc.)
-** Bartek (regex match) (account creation block) (blocked by: Sannse)
-on (etc.)
-:::: Changed already --[[User:Bartek|Bartek]] 09:34, 24 November 2006
-(UTC)
-*
-http://fp006.qa.wikia.com/index.php?title=Special:Regexblockstats&amp;target=sannse
-- I think all other logs have newes entries at the top. Can this one be
-reversed?
-:::: Done - --[[User:Bartek|Bartek]] 09:39, 24 November 2006 (UTC)
-* I think that's all I can come up with, this is wonderful! thanks so
-much for getting this going :) -- [[User:Sannse|Sannse]] 18:13, 23
-November 2006 (UTC)
-* The list of blocks is in alphabetical order. Is this the best way? I
-think it may be, I'm just used to seeing such things in time order.
-:::: Right. For the time being, it is in a descending time order. Feel
-free to test and I will change to what will be best. -
---[[User:Bartek|Bartek]] 14:03, 27 November 2006 (UTC)
-* Expired blocks are shown - useful? or are they quickly going to fill
-up the blocklog?
-:::: Now they are hidden. - --[[User:Bartek|Bartek]] 14:03, 27 November
-2006 (UTC)
-* All seems to work as advertised :) -- [[User:Sannse|Sannse]] 11:32,
-27 November 2006 (UTC)
-* A community team question. Do we want to change the default block
-reasons at all? The current defaults are:
-** "This IP address is prevented from editing due to vandalism" (IP
-block)
-** "This username is prevented from editing due to vandalism by a user
-with a similar name. Please create an alternate user name or contact
-Wikia about the problem" (RegEx block)
-** "This username is prevented from editing due to vandalism" (Username
-block)
-:It won't always be vandalism. Is there something more general we can
-use to cover vandalism, spamming, trolling, impersonation, etc?
-[[User:Angela|Angela]] 10:53, 28 November 2006 (UTC)
-:: how about "vandalism or other disruption" -- [[User:Sannse|Sannse]]
-08:59, 30 November 2006 (UTC)
-* as Splarka said earlier (05.12.2006) on IRC - ''an idea for blocking
-IP ranges: a radio box: (*) whole username (exact match) ( ) regex
-username ( ) IP or IP range in the first: "Splarka" would block just
-that name... in the second "Spla" would block any username containing
-that... and in the third: 75.72.55.78 would block just the user's IP,
-and 75.72. could block 75.72.*.* simple summary: IP matching would be
-made manual (easier to code probably?) although CIDR support might be
-better than regex for IPs... eg 75.72.0.0/16''
-* as Sannse and Splarka said today on IRC, it would be good to have an
-option to block users in case-sensitive mode. One proposition is for an
-exact match to be case-sensitive. - --[[User:Bartek|Bartek]] 09:06, 18
-December 2006 (UTC)
-:: As agreed, it will be case-sensitive for an exact match mode. -
---[[User:Bartek|Bartek]] 09:08, 18 December 2006 (UTC)
-:::Which is already under testing
-* and, as Emil said, it would be good to check if there is some proxy
-check within the IP check (might be already implemented in existing
-functions, since I am using class User's function to get the user's IP)
-==Suggested messages==
-* "This IP address is prevented from editing due to vandalism or other
-disruption by you or by someone who shares your IP address. If you
-believe this is in error, please [[Special:Contact|contact Wikia]]."
-(IP block)
-* "This username is prevented from editing due to vandalism or other
-disruption by a user with a similar name. Please create an alternate
-user name or [[Special:Contact|contact Wikia]] about the problem."
-(RegEx block)
-* "This username is prevented from editing due to vandalism or other
-disruption. If you believe this is in error, please
-[[Special:Contact|contact Wikia]]." (Username block)
-==Help page==
-[[Help:Global blocks]] (for contributors, not feature users)
-==Post release==
-A possible problem that's hard to confirm.
-User:WatchTVEatDonutDrinkBeer complained of errors on trying to access
-pages on wikiality. Their browser would direct them to
-http://84.40.25.253/PageName
-and they would get the 404 message: Not Found The requested URL
-/Main_Page was not found on this server. Apache/2.2.0 (Unix)
-mod_ssl/2.2.0 OpenSSL/0.9.7f PHP/5.1.4 Server at 84.40.25.253 Port 80
-Because the timing of their last edit was so similar to a block Angela
-had made: # wheels! (regex match) (account creation block) (blocked by:
-Angela, generic reason) on 19:43, 29 December 2006 (unblock) permanent
-block (stats)
-I tried removing this block. Once I'd done that, he could access the
-site normally. Replacing the block had no effect. I compared his IP
-with the vandal's (User:Willy on wheels!) but they didn't match.
-Perhaps a coincidence? But recording it here just in case it isn't --
-[[User:Sannse|Sannse]] 20:42, 29 December 2006 (UTC)
-[[Category:Features]]</text>
-    </revision>
-  </page>
-</mediawiki>
Index: trunk/extensions/regexBlock/RegexBlock.html
===================================================================
--- trunk/extensions/regexBlock/RegexBlock.html	(revision 45427)
+++ trunk/extensions/regexBlock/RegexBlock.html	(revision 45428)
@@ -1,416 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" dir="ltr" lang="en"><head>
-		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-		<link rel="copyright" href="http://www.gnu.org/copyleft/fdl.html"><title>RegexBlock</title>
-		
-		<style type="text/css" media="screen,projection">/*<![CDATA[*/ @import "/skins3/monobook/main.css?0"; /*]]>*/</style>
-		<link rel="stylesheet" type="text/css" media="print" href="_files/commonPrint.css"><!--[if lt IE 5.5000]><style type="text/css">@import "/skins3/monobook/IE50Fixes.css";</style><![endif]--><!--[if IE 5.5000]><style type="text/css">@import "/skins3/monobook/IE55Fixes.css";</style><![endif]--><!--[if IE 6]><style type="text/css">@import "/skins3/monobook/IE60Fixes.css";</style><![endif]--><!--[if IE 7]><style type="text/css">@import "/skins3/monobook/IE70Fixes.css?0";</style><![endif]--><!--[if lt IE 7]><script type="text/javascript" src="/skins3/common/IEFixes.js"></script>
-		<meta http-equiv="imagetoolbar" content="no" /><![endif]-->
-		
-		<script type="text/javascript">var skin = 'monobook';var stylepath = '/skins3';</script>
-		<script type="text/javascript" src="_files/wikibits"><!-- wikibits js --></script>
-		<script type="text/javascript" src="_files/index"><!-- site js --></script>
-		<style type="text/css">/*<![CDATA[*/
-@import "/index.php?title=MediaWiki:Common.css&action=raw&ctype=text/css&smaxage=18000";
-@import "/index.php?title=MediaWiki:Monobook.css&action=raw&ctype=text/css&smaxage=18000";
-@import "/index.php?title=-&action=raw&gen=css&maxage=18000&ts=20070117004616";
-/*]]>*/</style>
-		<script type="text/javascript" src="_files/index_002"></script><!-- Head Scripts --></head>
-
-<body class="ns-0">
-
-<div id="globalWrapper">
-		<div id="column-content">
-       <div id="content" class="content-ads">	 
-
-        	
-		<a name="top" id="top"></a>
-				<h1 class="firstHeading">RegexBlock</h1>
-                
-        
-		<div id="bodyContent">
-			<h3 id="siteSub">Wikia</h3>
-			<div id="contentSub"></div>
-												<!-- start content -->
-
-<table id="toc" class="toc" summary="Contents"><tbody><tr><td><div id="toctitle"><h2>Contents</h2> <span class="toctoggle">[<a href="javascript:toggleToc()" class="internal" id="togglelink">hide</a>]</span></div>
-<ul>
-<li class="toclevel-1"><a href="#Product_Requirements"><span class="tocnumber">1</span> <span class="toctext">Product Requirements</span></a>
-<ul>
-<li class="toclevel-2"><a href="#Product_Description"><span class="tocnumber">1.1</span> <span class="toctext">Product Description</span></a></li>
-<li class="toclevel-2"><a href="#Product_Assumptions"><span class="tocnumber">1.2</span> <span class="toctext">Product Assumptions</span></a></li>
-<li class="toclevel-2"><a href="#Product_Features"><span class="tocnumber">1.3</span> <span class="toctext">Product Features</span></a></li>
-<li class="toclevel-2"><a href="#Key_Product_Concepts"><span class="tocnumber">1.4</span> <span class="toctext">Key Product Concepts</span></a></li>
-<li class="toclevel-2"><a href="#Product_Unknowns"><span class="tocnumber">1.5</span> <span class="toctext">Product Unknowns</span></a></li>
-<li class="toclevel-2"><a href="#Potential_Product_Issues"><span class="tocnumber">1.6</span> <span class="toctext">Potential Product Issues</span></a></li>
-<li class="toclevel-2"><a href="#Product_Interface"><span class="tocnumber">1.7</span> <span class="toctext">Product Interface</span></a></li>
-<li class="toclevel-2"><a href="#Product_Structure"><span class="tocnumber">1.8</span> <span class="toctext">Product Structure</span></a></li>
-<li class="toclevel-2"><a href="#Product_Use_Cases"><span class="tocnumber">1.9</span> <span class="toctext">Product Use Cases</span></a></li>
-<li class="toclevel-2"><a href="#Product_Bugs"><span class="tocnumber">1.10</span> <span class="toctext">Product Bugs</span></a></li>
-<li class="toclevel-2"><a href="#Discussion"><span class="tocnumber">1.11</span> <span class="toctext">Discussion</span></a></li>
-<li class="toclevel-2"><a href="#Suggested_messages"><span class="tocnumber">1.12</span> <span class="toctext">Suggested messages</span></a></li>
-<li class="toclevel-2"><a href="#Help_page"><span class="tocnumber">1.13</span> <span class="toctext">Help page</span></a></li>
-<li class="toclevel-2"><a href="#Post_release"><span class="tocnumber">1.14</span> <span class="toctext">Post release</span></a></li>
-</ul>
-</li>
-</ul>
-</td></tr></tbody></table><script type="text/javascript"> if (window.showTocToggle) { var tocShowText = "show"; var tocHideText = "hide"; showTocToggle(); } </script>
-<a name="Product_Requirements"></a><h1>Product Requirements</h1>
-<a name="Product_Description"></a><h2>Product Description</h2>
-<dl><dd> This is the automated version of the original regexBlock
-extension. It takes data out of shared the database instead out of
-hardwritten text inside of the file (<i>uses memcached to speed up the operations, mostly on the side of checking edits</i>)
-and applies blocking to all users, groupped by blocker's names. There
-is also a Special Page (restricted to users with the preexisting
-'block' ability) that allows to block, view (also view by blockers) and
-unblock user names and IP addresses. Current version also allows to
-block entire IP addresses (no range or partial blockings in this case).
-</dd></dl>
-<dl><dd> Will also include in this one an ability to prevent new
-account creation (yes, such a thing already exists in IP Blocks, but is
-not multi-wikia-wide).
-</dd></dl>
-<a name="Product_Assumptions"></a><h2>Product Assumptions</h2>
-<ul><li> we use a shared database (so the block is enabled on multiple wikias)
-</li><li> we use memcached (so database operations are sped up)
-</li><li> user of this special page should have the rights to block
-</li><li> we take care of account creation prevention multi-wikia style right here
-</li></ul>
-<a name="Product_Features"></a><h2>Product Features</h2>
-<dl><dd><dl><dt>Basic functionality
-</dt><dd> Ability to block users by their names when they try to edit
-pages, getting data out of the database, special page for managing
-blocks.
-</dd><dt>Extended functionality
-</dt><dd> Ability to block IP addresses, use of memcached, viewing blocks by blockers.
-</dd><dt>Additional functionality
-</dt><dd> Ability to block new account creation on multiple wikias,
-displaying efficiency statistics for particular blocks (who, when, from
-where tried to edit and was blocked), allowing to set expiration period
-for blocks (which is already done).
-</dd></dl>
-</dd></dl>
-<a name="Key_Product_Concepts"></a><h2>Key Product Concepts</h2>
-<dl><dd><dl><dt>Data fetched from shared database
-</dt><dd> Data should be fetched from a shared database, supported with the use of memcached instead of hardwritten from the file.
-</dd><dt>Submit form on one page with the list
-</dt><dd> Easier to manage than having them on two separated special pages.
-</dd><dt>May be extended by having more statistics, timestamps, allow blocks by IP ranges, and such
-</dt><dd> We can add some statistics into separate tables (like the
-reason - rather an internal note for admin, why this particular user
-has been blocked, or the IP's from which the user tried to login etc.).
-</dd></dl>
-</dd></dl>
-<p><br>
-</p>
-<a name="Product_Unknowns"></a><h2>Product Unknowns</h2>
-<ul><li> Should we have a similar page for usernames excluded from
-blocking (when we block some IPs)? This could be linked to this project
-(the new special page would fetch some data from this one).
-</li><li> Include ability to block entire IP ranges? There are already indications that it would be useful.
-</li><li> Include timestamps? For now, the blocks are there for infinity.
-<ul><li> Timestamps are now included. Expired blocks are deleted
-automatically when a blocked user tries to edit and an expired block is
-then applied. In such case, the block is discarded and user can edit.
-Admin can also remove expired blocks manually from list (they are
-denoted 'EXPIRED' in red font)
-</li></ul>
-</li><li> Show more information? Like for example statistics, which users tried to login from which IPs and how many times, etc.
-</li></ul>
-<a name="Potential_Product_Issues"></a><h2>Potential Product Issues</h2>
-<ul><li> As we are able to block entire IP addresses, so there is more
-need for a utility to allow specific users identified by their
-usernames to edit anyway, even when they log from the blocked address.
-Similar request already exists here: <a>Product_Priorities:_Ops_Upgrades_Bugs#General</a>.
-The special page to exclude blocks could integrate both IP Blocks and
-Regexblocks or it could be just two separate special pages.
-</li><li> <strike>The blocks are permanent at the moment. So there is more attention required to them.</strike>
-</li></ul>
-<dl><dd> Which is untrue now. The blocks can be made with a chosen
-expiration time. After that, then can be removed manually or on block.
-Either way, they will be harmless.
-</dd></dl>
-<ul><li> also a note: on older versions of MW when the user is
-regexblocked (s)he cannot also create accounts (same goes for normal IP
-blocks), on newer (I've tested it personally on 1.8.2) it allows a
-separate user account creation block - it's about the code, and since I
-was developing a clean extension, I didn't want to alter the MW code
-itself. This leads to a situation, when the same block of one vandal
-will allow him/her to create account on one wiki, and disallow on the
-other (assuming that they will have different versions and the option
-'block account creation' will be unchecked).
-</li></ul>
-<a name="Product_Interface"></a><h2>Product Interface</h2>
-<dl><dd> Has one Special Page named Regexblock. It displays a form, used for adding blocks, and a list, used for managing blocks.
-</dd></dl>
-<dl><dd> The form has a field, specyfing user name or IP that we want
-to block. While not required for a successful block, there are three
-more options: whether match should be <b>exact</b>, whether we should deny this user <b>account creation</b>, or the <b>expiration period</b>, after which the block will be invalid.
-</dd></dl>
-<dl><dd> The list can be filtered by usernames of the blockers. Each row displays following information:  <i>username or IP address blocked</i>, <i>username of the blocker</i> and a <i>link to unblock</i>.
-The unblock requires no confirmation and is processed instantenuosly.
-The list can be traversed as all standard lists in special pages (prev,
-next, display by 20, 50 etc.). Each row also has the link to <b>statistics</b> for a particular block.
-</dd></dl>
-<a name="Product_Structure"></a><h2>Product Structure</h2>
-<ul><li> <b>Files</b>
-<ul><li> <b>regexBlock.php</b> <i>contains includes for the files in the folder, this one replaces old regexBlock.php</i>
-</li><li> <b>/regexBlock</b>  <i>folder containg the core of the extension plus docs</i>
-<ul><li> <b>README</b> <i>contains installation instructions and product description</i>
-</li><li> <b>regexBlockCore.php</b> <i>contains the blocking utility and use of the hook</i>
-</li><li> <b>SpecialRegexBlock.php</b> <i>Special Page for viewing and managing block list</i>
-</li><li> <b>SpecialRegexBlockStats.php</b> <i>Special Page for displaying stats for particular blocks</i>
-</li><li> <i> <b>SimplifiedRegex.php</b> - this one is required also by SpamRegex</i>
-</li></ul>
-</li></ul>
-</li></ul>
-<ul><li> <b>SQL</b>
-</li></ul>
-<pre>CREATE TABLE `blockedby` (
-`blckby_id` int(5) NOT NULL auto_increment,
-`blckby_name` varchar(255) character set latin1 collate latin1_bin NOT NULL,
-`blckby_blocker` varchar(255) character set latin1 collate latin1_bin NOT NULL,
-`blckby_timestamp` char(14) NOT NULL,
-`blckby_expire` char(14) NOT NULL,
-`blckby_create` tinyint(1) NOT NULL default '1',
-`blckby_exact` tinyint(1) NOT NULL default '0',
-`blckby_reason` tinyblob NOT NULL,
- PRIMARY KEY  (`blckby_id`),
- UNIQUE KEY `blckby_name` (`blckby_name`),
- KEY `blckby_timestamp` (`blckby_timestamp`),
- KEY `blckby_expire` (`blckby_expire`)
- )&nbsp;;
-</pre>
-<pre>CREATE TABLE `stats_blockedby` (
-`stats_id` int(8) NOT NULL auto_increment,
-`stats_user` varchar(255) character set latin1 collate latin1_bin NOT NULL,
-`stats_blocker` varchar(255) character set latin1 collate latin1_bin NOT NULL,
-`stats_timestamp` char(14) NOT NULL,
-`stats_ip` char(15) NOT NULL,
-`stats_match` varchar(255) character set latin1 collate latin1_bin NOT NULL default '-',
-`stats_wiki` varchar(255) NOT NULL default <i>,</i>
- PRIMARY KEY  (`stats_id`),
- KEY `stats_timestamp` (`stats_timestamp`)
- )&nbsp;;
-</pre>
-<p><br>
-</p>
-<ul><li> <b>Installation</b>
-</li></ul>
-<ol><li> Create tables in the shared database.
-</li><li> Copy folder /regexBlock to /extensions, copy SimplifiedRegex.php to /extensions (<b>do not</b> copy regexBlock.php yet!).
-</li><li> Edit regexBlock.php and set <b>REGEXBLOCK_PATH</b> to its correct value.
-</li><li> Copy regexBlock.php to /extensions.
-</li></ol>
-<dl><dd>That should do the trick. If people belonging to staff cannot
-access the extension, check if relogin helps. If not, please check if
-the staff permissions are not <b>overwritten</b> after the
-initialization of the extension. If that is the case, the permissions
-should be rather appended (the extension adds one new permission
-right).
-</dd></dl>
-<a name="Product_Use_Cases"></a><h2>Product Use Cases</h2>
-<ol><li> <b>Blocking a vandalising user by a username</b>
-</li></ol>
-<dl><dd><dl><dd>A user is vandalising over different wikias. An admin
-blocks the user by adding his/her username to the block list. The user
-cannot edit pages anymore until the block is released.
-<dl><dt>Step 1 (An admin blocks user)
-</dt><dd>Admin inserts the username into the database via the special page regexblock.
-</dd><dt>Step 2 (The user cannot edit pages)
-</dt><dd>Blocked user (a user that his/her username <b>contains</b> the
-blocked username) tries to edit. There goes a block check, and there is
-a match. Now, the expiration check is done. If the block is still
-valid, unfortunate (vandalising) user will be displayed a message that
-this particular user is blocked, along with the name of the blocker.
-Also, on a successful block, an entry into the database is made.
-</dd><dt>Step 3 (Admin releases the block)
-</dt><dd>When the situation is under control, admin can ublock the user
-(or not, if the user's actions made clear that the block should be
-permanent). Admin can also view statistics for this particular block
-(frequent attempts to edit despite being blocked may encourage admin to
-make a more permanent block...).
-</dd></dl>
-</dd></dl>
-</dd></dl>
-<ol><li> <b>Blocking a vandalising user by an IP address</b>
-</li></ol>
-<dl><dd><dl><dd> A user is vandalising over different wikias. An admin
-blocks the user by adding his/her IP address to the block list. No
-users logging from that IP will be able to edit the pages.
-<dl><dt>Step 1 (An admin blocks <b>full</b> IP address)
-</dt><dd>Admin inserts the <b>full</b> IP address into the database via the special page regexblock (<i>a
-partial or range IP address will be treated like a username. This is to
-avoid situations such as when a '3' block blocks all IP's including
-'3'. Regular expressions.</i>). 
-</dd><dt>Step 2 (The user cannot edit pages)
-</dt><dd>When a user logging in from a particular IP address (it has to be an <b>exact</b>
-match) tries to edit, he/she will be displayed a message that this
-particular user is blocked, along with the name of the blocker. Also,
-all non-logged users from that IP address will be blocked.
-</dd><dt>Step 3 (Admin releases the block)
-</dt><dd>When the situation is under control, admin can ublock this IP
-address (or not, if the user's actions made clear that the block should
-be permanent).
-</dd></dl>
-</dd></dl>
-</dd></dl>
-<a name="Product_Bugs"></a><h2>Product Bugs</h2>
-<dl><dd><b>Known Bugs</b>:
-</dd></dl>
-<dl><dd><ul><li> there is a little glitch when someone unblocks the
-last block by a particular blocker (it shows 'All' on the select, but
-the list is empty, the only exception is when unblocked row was the
-last on the global list)
-</li></ul>
-</dd></dl>
-<a name="Discussion"></a><h2>Discussion</h2>
-<p>Kaz3t</a>:
-I'm not convinced that currently way for blocking user by name is the
-best. For example user name can consist of his real name and surname.
-So if we block user "John" we also block users "Johnny", "John Smith",
-etc. In my opinion we should have choice - multi-block (as it is now)
-and single-block (for example when we want to block this and only this
-specified user we put his name between quotation-marks). </p>
-<dl><dd><dl><dd><dl><dd> Yes, you got a point. Umm, but wouldn't a checkbox be better? It would be more obvious on the first sight, I believe. --Bartek 16:03, 17 November 2006 (UTC)
-</dd><dd> I added a checkbox just for that, should be working fine. --Bartek 16:01, 20 November 2006 (UTC)
-</dd></dl>
-</dd></dl>
-</dd></dl>
-<ul><li> The message that a user sees is the same whether it is a
-similar name block or a specific name block (or even an IP block). I
-think this needs to either be made more general, or made variable. The
-reason set is: "This username is prevented from editing due to
-vandalism by a user with a similar name. Please create an alternate
-user name or contact Wikia about the problem"
-</li></ul>
-<dl><dd><dl><dd><dl><dd><dl><dd> There are four types of messages now
-(displayed to the blocked user). First, is the reason specified by an
-admin. If there is none, different messages will be displayed for:
-exact username match, IP address match and a regex username match. - --Bartek 13:07, 24 November 2006 (UTC)
-</dd></dl>
-</dd></dl>
-</dd></dl>
-</dd></dl>
-<ul><li> Is it possible to block <i>just</i> creation of accounts? for
-example to stop future creation of sannse-like accounts while still
-allowing me to edit? (this isn't vital though)
-</li></ul>
-<dl><dd><dl><dd><dl><dd><dl><dd> Good point. For the moment, it is not possible with this extension. It will be added in the future. - --Bartek 09:42, 24 November 2006 (UTC)
-</dd></dl>
-</dd></dl>
-</dd></dl>
-</dd></dl>
-<ul><li> When it's an exact match block, this is mentioned on the
-listing. Could it also be mentioned when it is not exact? It would make
-this listing clearer. For example:
-<ul><li>  Bartek (exact match) (account creation block) (blocked by: Sannse) on (etc.)
-</li><li>  Bartek (regex match) (account creation block) (blocked by: Sannse) on (etc.)
-</li></ul>
-</li></ul>
-<dl><dd><dl><dd><dl><dd><dl><dd> Changed already --Bartek 09:34, 24 November 2006 (UTC)
-</dd></dl>
-</dd></dl>
-</dd></dl>
-</dd></dl>
-<dl><dd><dl><dd><dl><dd><dl><dd> Done - --Bartek 09:39, 24 November 2006 (UTC)
-</dd></dl>
-</dd></dl>
-</dd></dl>
-</dd></dl>
-<ul><li> I think that's all I can come up with, this is wonderful! thanks so much for getting this going&nbsp;:) -- Sannse 18:13, 23 November 2006 (UTC)
-</li></ul>
-<ul><li> The list of blocks is in alphabetical order. Is this the best
-way? I think it may be, I'm just used to seeing such things in time
-order.
-</li></ul>
-<dl><dd><dl><dd><dl><dd><dl><dd> Right. For the time being, it is in a descending time order. Feel free to test and I will change to what will be best. - --Bartek 14:03, 27 November 2006 (UTC)
-</dd></dl>
-</dd></dl>
-</dd></dl>
-</dd></dl>
-<ul><li> Expired blocks are shown - useful? or are they quickly going to fill up the blocklog?
-</li></ul>
-<dl><dd><dl><dd><dl><dd><dl><dd> Now they are hidden. - --Bartek 14:03, 27 November 2006 (UTC)
-</dd></dl>
-</dd></dl>
-</dd></dl>
-</dd></dl>
-<ul><li> All seems to work as advertised&nbsp;:) -- Sannse 11:32, 27 November 2006 (UTC)
-</li></ul>
-<ul><li> A community team question.  Do we want to change the default block reasons at all?  The current defaults are:
-<ul><li> "This IP address is prevented from editing due to vandalism" (IP block)
-</li><li> "This username is prevented from editing due to vandalism by
-a user with a similar name. Please create an alternate user name or
-contact Wikia about the problem" (RegEx block)
-</li><li> "This username is prevented from editing due to vandalism" (Username block)
-</li></ul>
-</li></ul>
-<dl><dd>It won't always be vandalism. Is there something more general
-we can use to cover vandalism, spamming, trolling, impersonation, etc? Angela 10:53, 28 November 2006 (UTC)
-</dd></dl>
-<dl><dd><dl><dd> how about "vandalism or other disruption" -- Sannse 08:59, 30 November 2006 (UTC)
-</dd></dl>
-</dd></dl>
-<ul><li> as Splarka said earlier (05.12.2006) on IRC - <i>an idea for
-blocking IP ranges: a radio box: (*) whole username (exact match) ( )
-regex username ( ) IP or IP range in the first: "Splarka" would block
-just that name... in the second "Spla" would block any username
-containing that... and in the third: 75.72.55.78 would block just the
-user's IP, and 75.72. could block 75.72.*.* simple summary: IP matching
-would be made manual (easier to code probably?) although CIDR support
-might be better than regex for IPs... eg 75.72.0.0/16</i>
-</li></ul>
-<ul><li> as Sannse and Splarka said today on IRC, it would be good to
-have an option to block users in case-sensitive mode. One proposition
-is for an exact match to be case-sensitive. - --Bartek 09:06, 18 December 2006 (UTC)
-</li></ul>
-<dl><dd><dl><dd> As agreed, it will be case-sensitive for an exact match mode. - --Bartek 09:08, 18 December 2006 (UTC)
-<dl><dd>Which is already under testing
-</dd></dl>
-</dd></dl>
-</dd></dl>
-<ul><li> and, as Emil said, it would be good to check if there is some
-proxy check within the IP check (might be already implemented in
-existing functions, since I am using class User's function to get the
-user's IP)
-</li></ul>
-<a name="Suggested_messages"></a><h2>Suggested messages</h2>
-<ul><li> "This IP address is prevented from editing due to vandalism or
-other disruption by you or by someone who shares your IP address. If
-you believe this is in error, please contact Wikia." (IP block)
-</li><li> "This username is prevented from editing due to vandalism or
-other disruption by a user with a similar name. Please create an
-alternate user name or contact Wikia about the problem." (RegEx block)
-</li><li> "This username is prevented from editing due to vandalism or other disruption. If you believe this is in error, please contact Wikia." (Username block)
-</li></ul>
-<a name="Post_release"></a><h2>Post release</h2>
-<p>A possible problem that's hard to confirm.
-User:WatchTVEatDonutDrinkBeer complained of errors on trying to access
-pages on wikiality. Their browser would direct them to Not Found
-
-The requested URL /Main_Page was not found on this server.
-Apache/2.2.0 (Unix) mod_ssl/2.2.0 OpenSSL/0.9.7f PHP/5.1.4 Server at 84.40.25.253 Port 80
-<p>Because the timing of their last edit was so similar to a block Angela had made:
-</p>
-<pre># wheels! (regex match) (account creation block) (blocked by: Angela, generic reason) on 19:43, 29 December 2006 (unblock) permanent block (stats)
-</pre>
-<p>I tried removing this block. Once I'd done that, he could access the
-site normally. Replacing the block had no effect. I compared his IP
-with the vandal's (User:Willy on wheels!) but they didn't match.
-Perhaps a coincidence? But recording it here just in case it isn't -- Sannse 20:42, 29 December 2006 (UTC)
-</p>
-
-	</div>
-       </div>
-
-			<div class="visualClear"></div>
-		<div id="footer">
-				<div id="f-poweredbyico"><a href="http://www.mediawiki.org/"><img src="_files/poweredby_mediawiki_88x31" alt="MediaWiki"></a></div>
-				<div id="f-copyrightico"><a href="http://www.gnu.org/copyleft/fdl.html"><img src="_files/gnu-fdl" alt="GNU Free Documentation License"></a></div>
-	  			<div id="f-hosting"><i>Wikia</i> is a service mark of Wikia, Inc. All rights reserved.</div>
-		</div>
-
-</div>
-
-</body></html>
Index: trunk/extensions/regexBlock/SpecialRegexBlockStats.php
===================================================================
--- trunk/extensions/regexBlock/SpecialRegexBlockStats.php	(revision 45427)
+++ trunk/extensions/regexBlock/SpecialRegexBlockStats.php	(revision 45428)
@@ -1,113 +0,0 @@
-<?php
-
-/**#@+
- * Extension used for blocking users names and IP addresses with regular expressions. Contains both the blocking mechanism and a special page to add/manage blocks
- *
- * @addtogroup SpecialPage
- *
- * @author Bartek Łapiński
- * @copyright Copyright © 2007, Wikia Inc.
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
-*/
-
-if(!defined('MEDIAWIKI'))
-	die();
-
-/* add data to tables */
-$wgExtensionFunctions[] = 'wfRegexBlockStatsPageSetup';
-$wgExtensionCredits['specialpage'][] = array(
-	'name' => 'Regex Block Stats',
-	'author' => 'Bartek Łapiński',
-	'url' => 'http://www.mediawiki.org/wiki/Extension:RegexBlock',
-	'description' => 'Displays block statistics for the regexBlock extension',
-	'descriptionmsg' => 'regexblock-stat-desc',
-);
-
-/* special page setup function */
-function wfRegexBlockStatsPageSetup () {
-	global $IP;
-	if (!wfSimplifiedRegexCheckSharedDB())
-		return;
-	require_once($IP. '/includes/SpecialPage.php');
-	/* name, restrictions, */
-	SpecialPage::addPage(new SpecialPage('Regexblockstats', 'regexblock', false, 'wfRegexBlockStatsCore', false));
-}
-
-/* special page core function */
-function wfRegexBlockStatsCore () {
-	global $wgOut, $wgUser, $wgRequest;
-	$wgOut->setPageTitle (wfMsg('regexblock-stats-title'));
-	$username = $wgRequest->getVal('target');
-	$wgOut->setSubtitle (wfMsg('regexblock-stats-username', $username));	
-	$scL = new RegexBlockStatsList();
-	$scL->showList('', $username);
-}
-
-/* list class  */
-class RegexBlockStatsList {
-	var $numResults;
-
-	/* constructor */
-	function RegexBlockStatsList () {
-		$this->numResults = 0 ;
-	} 
-	
-	/* show it up */
-	function showList ($error, $username) {
-		global $wgOut, $wgRequest;
-		/* no list when no user */
-		if ("" == $username)
-			return false;
-		$this->fetchNumResults($username);
-		$this->showPrevNext($wgOut);
-		$wgOut->addHTML("<ul>"); 
-		$this->fetchLogs($username);
-		$wgOut->addHTML("</ul>"); 
-		$this->showPrevNext($wgOut);		
-	}
-
-	/* fetch number of all rows */
-	function fetchNumResults ($username) {
-		global $wgMemc, $wgSharedDB;
-		$dbr = &wfGetDB (DB_SLAVE);
-		$query_count = "SELECT COUNT(*) as n FROM ".wfRegexBlockGetStatsTable()." WHERE stats_user = ".$dbr->addQuotes ($username);
-		$res_count = $dbr->query($query_count);
-		$row_count = $dbr->fetchObject ($res_count);
-		$this->numResults = $row_count->n;
-		$dbr->freeResult ($res_count);
-	}
-
-	/* fetch all logs */
-	function fetchLogs ($username) {
-		global $wgOut, $wgSharedDB, $wgDBname, $wgRequest, $wgLang;
-		/* from database */
-		list( $limit, $offset ) = $wgRequest->getLimitOffset();
-		$dbr =& wfGetDB (DB_SLAVE);
-		$query = "SELECT stats_user, stats_blocker, stats_timestamp, stats_ip 
-			  FROM ".wfRegexBlockGetStatsTable()." 
-			  WHERE stats_user={$dbr->addQuotes($username)} 
-			  ORDER BY stats_timestamp DESC LIMIT $offset,$limit";
-		$res = $dbr->query($query);			
-		while ($row = $dbr->fetchObject($res)) {
-			$time = $wgLang->timeanddate( wfTimestamp( TS_MW, $row->stats_timestamp ), true );
-			$wgOut->addHTML("<li><b>{$row->stats_user}</b> ".wfMsg('regexblock-stats-times')." <b>{$time}</b>, ".wfMsg('regexblock-stats-logging')." <b>{$row->stats_ip}</b></li>");
-		}
-		$dbr->freeResult($res);			
-	}
-
-	/* init for showprevnext */
-	function showPrevNext( &$out ) {
-		global $wgContLang, $wgRequest;
-		list( $limit, $offset ) = $wgRequest->getLimitOffset();
-		$target = 'target=' . urlencode ( $wgRequest->getVal('target') );
-		$mode = '&mode=' . urlencode ( $wgRequest->getVal('mode') );
-		$html = wfViewPrevNext(
-			$offset,
-			$limit,
-			$wgContLang->specialpage( 'Regexblockstats' ),
-			$target.$mode,
-			($this->numResults - $offset) <= $limit
-		);
-		$out->addHTML( '<p>' . $html . '</p>' );
-	}
-}
\ No newline at end of file
Index: trunk/extensions/regexBlock/patch-upgrade.sql
===================================================================
--- trunk/extensions/regexBlock/patch-upgrade.sql	(revision 0)
+++ trunk/extensions/regexBlock/patch-upgrade.sql	(revision 45428)
@@ -0,0 +1,4 @@
+ALTER TABLE stats_blockedby ADD COLUMN `stats_blckby_id` int(8) NOT NULL;
+ALTER TABLE stats_blockedby ADD COLUMN `stats_match` varchar(255) NOT NULL default '';
+ALTER TABLE stats_blockedby ADD COLUMN `stats_dbname` varchar(255) NOT NULL default '';
+ALTER TABLE stats_blockedby ADD KEY `stats_blckby_id_key` (`stats_blckby_id`);
\ No newline at end of file

Property changes on: trunk/extensions/regexBlock/patch-upgrade.sql
___________________________________________________________________
Name: svn:eol-style
   + native

Index: trunk/extensions/regexBlock/regexBlockCore.php
===================================================================
--- trunk/extensions/regexBlock/regexBlockCore.php	(revision 45427)
+++ trunk/extensions/regexBlock/regexBlockCore.php	(revision 45428)
@@ -1,234 +1,605 @@
 <?php
-/**#@+
+/**
  * Extension used for blocking users names and IP addresses with regular expressions. Contains both the blocking mechanism and a special page to add/manage blocks
  *
- * @addtogroup SpecialPage
- *
- * @author Bartek Łapiński
+ * @file
+ * @ingroup Extensions
+ * @author Bartek Łapiński <bartek(at)wikia-inc.com>
+ * @author Piotr Molski <moli@wikia-inc.com>
+ * @author Adrian 'ADi' Wieczorek <adi(at)wikia-inc.com>
  * @copyright Copyright © 2007, Wikia Inc.
  * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 */
 
-/* add hook */
-global $wgHooks;
-$wgHooks['GetBlockedStatus'][] = 'wfRegexBlockCheck';
+/**
+ * Prepare data by getting blockers 
+ * @param $current_user User: current user  
+ */
+function wfRegexBlockCheck( $current_user ) {
+	wfProfileIn( __METHOD__ );
 
-/* 
-	prepare data by getting blockers 
-	@param $current_user User: current user  
-*/
-function wfRegexBlockCheck ($current_user) {
-	global $wgMemc, $wgSharedDB;
-	if (!wfSimplifiedRegexCheckSharedDB())
+	$ip_to_check = wfGetIP();
+
+	/* First check cache */
+	$blocked = wfRegexBlockIsBlockedCheck( $current_user, $ip_to_check );
+	if ( $blocked ) {
+		wfProfileOut( __METHOD__ );
 		return true;
-	$ip_to_check = wfGetIP();
-	$key = "$wgSharedDB:regexBlockCore:blockers";
-	$cached = $wgMemc->get($key);
-	if (!is_array($cached)) {
+	}
+	$blockers_array = wfRegexBlockGetBlockers();
+	$block_data = wfGetRegexBlockedData( $current_user, $blockers_array );
+
+	/* check user for each blocker */
+	foreach( $blockers_array as $blocker ) {
+		$blocker_block_data = isset( $block_data[$blocker] ) ? $block_data[$blocker] : null;
+		wfGetRegexBlocked( $blocker, $blocker_block_data, $current_user, $ip_to_check );
+	}
+
+	wfProfileOut( __METHOD__ );
+	return true;
+}
+
+/**
+ * Get blockers 
+ */
+function wfRegexBlockGetBlockers( $master = 0 ) {
+	global $wgSharedDB;
+	$oMemc = wfGetCache( CACHE_MEMCACHED );
+	wfProfileIn( __METHOD__ );
+
+	$key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_BLOCKERS_KEY );
+	$cached = $oMemc->get($key);
+	$blockers_array = array();
+
+	if ( !is_array( $cached ) ) {
 		/* get from database */
-		$blockers_array = array();
-		$dbr =& wfGetDB (DB_SLAVE);	
-		$query = "SELECT blckby_blocker FROM ".wfRegexBlockGetTable()." GROUP BY blckby_blocker";
-		$res = $dbr->query($query);
-		while ( $row = $dbr->fetchObject( $res ) ) {
-			wfGetRegexBlocked ($row->blckby_blocker, $current_user, $ip_to_check);
-			array_push ($blockers_array, $row->blckby_blocker);
+		$dbr = wfGetDB( ( empty( $master ) ) ? DB_SLAVE : DB_MASTER );
+		$oRes = $dbr->select( REGEXBLOCK_TABLE,
+			array("blckby_blocker"),
+			array("blckby_blocker <> ''"),
+			__METHOD__,
+			array("GROUP BY" => "blckby_blocker")
+		);
+		while( $oRow = $dbr->fetchObject($oRes) ) {
+			$blockers_array[] = $oRow->blckby_blocker;
 		}
-		$dbr->freeResult($res);
-		$wgMemc->set($key, $blockers_array, REGEXBLOCK_EXPIRE);
+		$dbr->freeResult($oRes);
+		$oMemc->set( $key, $blockers_array, REGEXBLOCK_EXPIRE );
 	} else {
 		/* get from cache */
-		foreach ($cached as $blocker) {
-			wfGetRegexBlocked ($blocker, $current_user, $ip_to_check);
-		}		
+		$blockers_array = $cached;
 	}
-	return true;
+
+	wfProfileOut( __METHOD__ );
+	return $blockers_array;
 }
 
-/* 
-	fetch usernames or IP addresses to run a match against
-	@param $blocker String: the admin who blocked
-	@param $user User: current user
-	@param $mode integer: REGEXBLOCK_MODE_IPS or REGEXBLOCK_MODE_NAMES
-	@return String: string to run a regex match against
-*/
-function wfGetRegexBlockedData ($blocker, $user, $mode) {
-	global $wgMemc, $wgUser, $wgSharedDB;
-	$names = "";
-	$first = true;
+/**
+ * Check if user is blocked
+ *
+ * @param $user User
+ * @param $ip 
+ * @return Array: an array of arrays to run a regex match against
+ */
+function wfRegexBlockIsBlockedCheck( $user, $ip ) {
+	global $wgSharedDB;
+	$oMemc = wfGetCache( CACHE_MEMCACHED );
 
-	/* first, check if regex string is already stored in memcache */
-	$key = str_replace( " ", "_", "$wgSharedDB:regexBlockCore:$mode:blocker:$blocker" );
-	$cached = $wgMemc->get($key);
-	if ( "" == $cached ) {
-		/* fetch data from db, concatenate into one string, then fill cache */
-		$dbr =& wfGetDB( DB_SLAVE );
-		$query = "SELECT blckby_name, blckby_exact FROM ".wfRegexBlockGetTable()." WHERE blckby_blocker = {$dbr->addQuotes($blocker)}";
-		$res = $dbr->query($query);
-		while ( $row = $dbr->fetchObject( $res ) ) {
-			$concat = "";
-			$is_ip = $user->isIP($row->blckby_name);
-			/* IPs are checked in exact mode, marked as exact also */
-			$simplified = $row->blckby_name;
-			if (( (REGEXBLOCK_MODE_IPS == $mode) && ($is_ip != 0) ) || $row->blckby_exact) {
-				$concat = "^{$simplified}$";
-			} else if ((REGEXBLOCK_MODE_NAMES == $mode)) {
-				$concat = $simplified;	
+	wfProfileIn( __METHOD__ );
+	$result = false;
+
+	$key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_USER_KEY, str_replace( ' ', '_', $user->getName() ) );
+	$cached = $oMemc->get( $key );
+	
+	if ( is_object( $cached ) ) {
+		$ret = wfRegexBlockExpireNameCheck( $cached );
+		if ( ( $ret !== false ) && ( is_array( $ret ) ) ) {
+			$ret['match'] = $user->getName();
+			$ret['ip'] = 0;
+			$result = wfRegexBlockSetUserData( $user, $ip, $ret['blocker'], $ret );
+		}
+	}
+
+	if ( ( $result === false ) && ( $ip != $user->getName() ) ) {
+		$key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_USER_KEY, str_replace( ' ', '_', $ip ) );
+		$cached = $oMemc->get( $key );
+		if ( is_object( $cached ) ) {
+			$ret = wfRegexBlockExpireNameCheck( $cached );
+			if ( ( $ret !== false ) && ( is_array( $ret ) ) ) {
+				$ret['match'] = $ip;
+				$ret['ip'] = 1;
+				$result = wfRegexBlockSetUserData($user, $ip, $ret['blocker'], $ret);
 			}
-			if ($concat != "") {
-				if (!$first) {
-					$names .= "|".$concat;	
-				} else {
-					$names .= $concat;
-					$first = false;
+		}
+	}
+
+	wfProfileOut( __METHOD__ );
+	return $result;
+}
+
+function wfRegexBuildExpression( $lines, $exact = 0, $batchSize = 4096 ) {
+	global $useSpamRegexNoHttp;
+
+	wfProfileIn( __METHOD__ );
+	/* Make regex */
+	$regexes = array();
+	$regexStart = ($exact) ? '/^(' : '/(';
+	$regexEnd = '';
+	if ( !empty( $exact ) ) { 
+		$regexEnd = ')$/';
+	} elseif ( $batchSize > 0 ) {
+		$regexEnd = ')/Si';
+	} else {
+		$regexEnd = ')/i';
+	}
+	$build = false;
+	foreach( $lines as $line ) {
+		if( $build == '' ) {
+			$build = $line;
+		} elseif( strlen( $build ) + strlen( $line ) > $batchSize ) {
+			$regexes[] = /*$regexStart . */str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $build) ) /*. $regexEnd*/;
+			$build = $line;
+		} else {
+			$build .= '|';
+			$build .= $line;
+		}
+	}
+
+	if( $build !== false ) {
+		$regexes[] = /*$regexStart . */str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $build) ) /*. $regexEnd*/;
+	}
+
+	wfProfileOut( __METHOD__ );
+	return $regexes;
+}
+
+function wfRegexIsCorrectCacheValue( $cached ) {
+	$result = false;
+	if ( empty( $cached ) ) {
+		$result = true;
+	} else {
+		$loop = 0;
+		$names = array('ips' => '', 'exact' => '', 'regex' => '');
+		foreach( $names as $key => $value ) {
+			if ( array_key_exists($key, $cached) && ( !empty( $cached[$key] ) ) ) {
+				$loop++;
+			}
+		}
+		if ( $loop == 0 ) {
+			$result = true;
+		}
+	}
+	return $result;
+}
+
+/**
+ * Fetch usernames or IP addresses to run a match against
+ *
+ * @param $blocker String: the admin who blocked
+ * @param $user User: current user
+ * @return Array: an array of arrays to run a regex match against
+ */
+function wfGetRegexBlockedData( $blocker, $user, $master = 0 ) {
+	global $wgSharedDB;
+	$oMemc = wfGetCache( CACHE_MEMCACHED );
+
+	wfProfileIn( __METHOD__ );
+	$blockData = array();
+
+	/**
+	 * First, check if regex strings are already stored in memcached
+	 * we will store entire array of regex strings here
+	 */
+	if ( !( $user instanceof User ) ) {
+		wfProfileOut( __METHOD__ );
+		return false;
+	}
+
+	$memkey = wfForeignMemcKey( ( isset( $wgSharedDB)) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_BLOCKERS_KEY, "All-In-One" );
+	$cached = $oMemc->get( $memkey );
+
+	if ( empty( $cached ) ) {
+		/* Fetch data from DB, concatenate into one string, then fill cache */
+		$dbr = wfGetDB( ( empty( $master ) ) ? DB_SLAVE : DB_MASTER );
+
+		foreach( $blockers as $blocker ) {
+			$oRes = $dbr->select(
+				REGEXBLOCK_TABLE,
+				array("blckby_id", "blckby_name", "blckby_exact"),
+				array("blckby_blocker = {$dbr->addQuotes($blocker)}"),
+				__METHOD__
+			);
+
+			$loop = 0;
+			$names = array( 'ips' => '', 'exact' => '', 'regex' => '' );
+			while ( $oRow = $dbr->fetchObject( $oRes ) ) {
+				$key = 'regex';
+				if ( $user->isIP($oRow->blckby_name) != 0 ) {
+					$key = 'ips';
+				} elseif ( $oRow->blckby_exact != 0 ) {
+					$key = 'exact';
 				}
+				$names[$key][] = $oRow->blckby_name;
+				$loop++;
 			}
+			$dbr->freeResult($oRes);
+
+			if ( $loop > 0 ) {
+				$blockData[$blocker] = $names;
+			}
 		}
-		$wgMemc->set($key, $names, REGEXBLOCK_EXPIRE); 
-		$dbr->freeResult($res);
+		$oMemc->set( $memkey, $blockData, REGEXBLOCK_EXPIRE );
 	} else {
-		/* take from cache */
-		$names = $cached;
-	}	
-	return $names;
+		/* take it from cache */
+		$blockData = $cached;
+	}
+
+	wfProfileOut( __METHOD__ );
+	return $blockData;
 }
 
-/*
-	check if the block expired or not (AFTER we found an existing block)
-	@param $user User: current user object
-	@param $names Array: matched names
-	@param $ips Array: matched ips
-	@return Array or false
-*/
-function wfRegexBlockExpireCheck ($user, $names = null, $ips = null) {	
-	global $wgMemc, $wgSharedDB;
-	/* I will use memcache, with the key being particular block */
-	if ( is_array ($ips) ) {
-		$array_match = $ips;
-		$username = wfGetIP();
-	} else {
-		$array_match = $names;
-		$username = $user->getName();
+/**
+  * Perform a match against all given values 
+  *
+  * @param $matching Array: array of strings containing list of values
+  * @param $value String: a given value to run a match against
+  * @param $exact Boolean: whether or not perform an exact match
+  * @return Array of matched values or false
+  */
+function wfRegexBlockPerformMatch( $matching, $value ) {
+	wfProfileIn( __METHOD__ );
+	$matched = array();
+
+	if ( !is_array( $matching ) ) {
+		/* empty? begone! */
+		wfProfileOut( __METHOD__ );
+		return false;
 	}
+
+	/* normalise for regex */
+	$loop = 0;
+	$match = array();
+	foreach( $matching as $one ) { 
+		/* the real deal */
+		$found = preg_match('/'.$one.'/i', $value, $match);
+		if ( $found ) {
+			if ( is_array( $match ) && ( !empty( $match[0] ) ) ) {
+				$matched[] = $one;
+				break;
+			}
+		}
+	}
+
+	wfProfileOut( __METHOD__ );
+	return $matched;
+}
+
+/**
+ * Check if the block expired or not (AFTER we found an existing block)
+ *
+ * @param $user User: current user object
+ * @param $array_match Boolean
+ * @param $names Array: matched names
+ * @param $ips Array: matched ips
+ * @return Array or false
+ */
+function wfRegexBlockExpireCheck( $user, $array_match = null, $ips = 0, $iregex = 0 ) {	
+	global $wgSharedDB;
+	$oMemc = wfGetCache( CACHE_MEMCACHED );
+	wfProfileIn( __METHOD__ );
+	/* I will use memcached, with the key being particular block */
+	if ( empty( $array_match ) ) {
+		wfProfileOut( __METHOD__ );
+		return false;
+	}
+
 	$ret = array();
-	$dbr =& wfGetDB (DB_SLAVE);
-	/* for EACH match check whether timestamp expired until found VALID timestamp
-	   but: only for a BLOCKED user, and it will be memcached 
-	   moreover, expired blocks will be consequently deleted
-	*/
-	foreach ($array_match as $single) {
-		$key = str_replace( " ", "_", "$wgSharedDB:regexBlockCore:blocked:$single" );
-		$cached = $wgMemc->get($key);
-		if ( !is_object ($cached) ) {
+	/**
+	 * For EACH match check whether timestamp expired until found VALID timestamp
+	 * but: only for a BLOCKED user, and it will be memcached 
+	  * moreover, expired blocks will be consequently deleted
+	 */
+	$blocked = '';
+	foreach( $array_match as $single ) {
+		$key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_USER_KEY, str_replace( ' ', '_', $single ) );
+		$blocked = null;
+		$cached = $oMemc->get( $key );
+		if ( empty( $cached ) || ( !is_object( $cached ) ) ) {
 			/* get from database */
-			$query = "SELECT blckby_timestamp, blckby_expire, blckby_blocker, blckby_create, blckby_exact, blckby_reason 
-				  FROM ".wfRegexBlockGetTable()." 
-				  WHERE blckby_name like {$dbr->addQuotes('%'.$single.'%')}";
-
-			$res = $dbr->query($query);
-			if ($row = $dbr->fetchObject ($res) ) {
+			$dbr = wfGetDB( DB_MASTER );
+			$where = array("blckby_name LIKE '%{$single}%'");
+			if ( !empty($iregex) ) {
+				$where = array("blckby_name = " . $dbr->addQuotes($single));
+			}
+			$oRes = $dbr->select( REGEXBLOCK_TABLE,
+				array("blckby_id", "blckby_timestamp", "blckby_expire", "blckby_blocker", "blckby_create", "blckby_exact", "blckby_reason"),
+				$where,
+				__METHOD__
+			);
+			if ( $oRow = $dbr->fetchObject( $oRes ) ) {
 				/* if still valid or infinite, ok to block user */
-				if ((wfTimestampNow () <= $row->blckby_expire) || ('infinite' == $row->blckby_expire)) {
-					$ret['create'] = $row->blckby_create;
-					$ret['exact'] = $row->blckby_exact;
-					$ret['reason'] = $row->blckby_reason; 
-					$wgMemc->set($key, $row);
-					$dbr->freeResult($res);
-					return $ret;
-				} else {  /* clean up an obsolete block */
-					wfRegexBlockClearExpired ($single, $row->blckby_blocker);
-				}
+				$blocked = $oRow;
 			}
-			$dbr->freeResult($res);
+			$dbr->freeResult ($oRes);
 		} else {
 			/* get from cache */
- 			if ((wfTimestampNow () <= $cached->blckby_expire) || ('infinite' == $cached->blckby_expire)) {
-				$ret['create'] = $cached->blckby_create;
-				$ret['exact'] = $cached->blckby_exact;
-				$ret['reason'] = $cached->blckby_reason;
+			$blocked = $cached;
+		}
+
+		/* check conditions */
+		if ( is_object($blocked) ) {
+			$ret = wfRegexBlockExpireNameCheck($blocked);
+			if ( $ret !== false ) {
+				$ret['match'] = $single;
+				$ret['ip'] = $ips;
+				$oMemc->set($key, $blocked);
+				wfProfileOut( __METHOD__ );
 				return $ret;
-			} else {  /* clean up an obsolete block */
-				wfRegexBlockClearExpired ($single, $cached->blckby_blocker);
+			} else {  
+				/* clean up an obsolete block */
+				wfRegexBlockClearExpired($single, $blocked->blckby_blocker);
 			}
 		}
 	}
+
+	wfProfileOut( __METHOD__ );
 	return false;
 }
 
-/* clean up an existing expired block 
-   @param $username String: name of the user
-   @param $blocker String: name of the blocker 
+/**
+ * Check if the USER block expired or not (AFTER we found an existing block)
+ *
+ * @param $blocked: block object
+ * @return Array or false
+ */
+function wfRegexBlockExpireNameCheck( $blocked ) {
+	$ret = false;
+	wfProfileIn( __METHOD__ );
+	if( is_object($blocked) ) {
+		if ( (wfTimestampNow () <= $blocked->blckby_expire) || ('infinite' == $blocked->blckby_expire) ) {
+			$ret = array(
+				'blckid' => $blocked->blckby_id,
+				'create' => $blocked->blckby_create,
+				'exact'  => $blocked->blckby_exact,
+				'reason' => $blocked->blckby_reason,
+				'expire' => $blocked->blckby_expire,
+				'blocker'=> $blocked->blckby_blocker,
+				'timestamp' => $blocked->blckby_timestamp
+			);
+		} 
+	}
+	wfProfileOut( __METHOD__ );
+	return $ret;
+}
 
-*/
-function wfRegexBlockClearExpired ($username, $blocker) {
-	$dbw =& wfGetDB( DB_MASTER );
-	$query = "DELETE FROM ".wfRegexBlockGetTable()." WHERE blckby_name = ".$dbw->addQuotes($username);
-	$dbw->query($query);
+/**
+ * Clean up an existing expired block
+ *
+ * @param $username String: name of the user
+ * @param $blocker String: name of the blocker 
+ */
+function wfRegexBlockClearExpired( $username, $blocker ) {
+	wfProfileIn( __METHOD__ );
+	$result = false;
+
+	$dbw = wfGetDB( DB_MASTER );
+
+	$dbw->delete( REGEXBLOCK_TABLE, 
+		array("blckby_name = {$dbw->addQuotes($username)}"),
+		__METHOD__
+	);
+
 	if ( $dbw->affectedRows() ) {
 		/* success, remember to delete cache key  */
-		wfRegexBlockUnsetKeys ($blocker, $username);
-		return true;
+		wfRegexBlockUnsetKeys( $username );
+		$result = true;
 	}
-	return false;
+
+	wfProfileOut( __METHOD__ );
+	return $result;
 }
 
-/* put the stats about block into database 
-   @param $username String
-   @param $user_ip String: IP of the current user
-   @param $blocker String
-*/
-function wfRegexBlockUpdateStats ($username, $user_ip, $blocker) {
-	global $wgSharedDB;
-	$dbw =& wfGetDB( DB_MASTER );
-	$now = wfTimestampNow();
-	$query = "INSERT INTO ".wfRegexBlockGetStatsTable()." 
-		  (stats_id, stats_user, stats_ip, stats_blocker, stats_timestamp) 
-		   values (null, {$dbw->addQuotes($username)}, '{$user_ip}',{$dbw->addQuotes($blocker)},'{$now}')";
-	$res = $dbw->query($query);
-	if ( $dbw->affectedRows()  ) {
-		return true;
+/**
+ * Put the stats about block into database
+ *
+ * @param $username String
+ * @param $user_ip String: IP of the current user
+ * @param $blocker String
+ * @param $match
+ * @param $blckid
+ */
+function wfRegexBlockUpdateStats( $user, $user_ip, $blocker, $match, $blckid ) {
+	global $wgSharedDB, $wgDBname;
+
+	$result = false;
+	wfProfileIn( __METHOD__ );
+
+	$dbw = wfGetDB( DB_MASTER );
+	$dbw->insert( REGEXBLOCK_STATS_TABLE, 
+		array(
+			'stats_id' => 'null',
+			'stats_blckby_id' => $blckid,
+			'stats_user' => $user->getName(),
+			'stats_ip' => $user_ip,
+			'stats_blocker' => $blocker,
+			'stats_timestamp' => wfTimestampNow(),
+			'stats_match' => $match,
+			'stats_dbname' => $wgDBname
+		),
+		__METHOD__
+	);
+
+	if ( $dbw->affectedRows() ) {
+		$result = true;
 	}
-	return false;
+
+	wfProfileOut( __METHOD__ );
+	return $result;
 }
 
-/* 	
-  the actual blocking goes here, for each blocker
-  @param $blocker String
-  @param $user User
-  @param $user_ip String
-*/
-function wfGetRegexBlocked ($blocker, $user, $user_ip) {
-	global $wgContactLink;
-	$names = wfGetRegexBlockedData ($blocker, $user, REGEXBLOCK_MODE_NAMES);
-	$ips = wfGetRegexBlockedData ($blocker, $user, REGEXBLOCK_MODE_IPS);
-	$username = $user->getName();
-	$matched_name = preg_match ('/'.$names.'/i', $user->getName(), $matches);
-	$matched_ip = preg_match ('/'.$ips.'/i', $user_ip, $ip_matches );
+/**
+ * The actual blocking goes here, for each blocker
+ *
+ * @param $blocker String
+ * @param $blocker_block_data
+ * @param $user User
+ * @param $user_ip String
+ */
+function wfGetRegexBlocked( $blocker, $blocker_block_data, $user, $user_ip ) {
+	wfProfileIn( __METHOD__ );
 
-	if ( ( $matched_name && ($names != "") ) || ( $matched_ip  && ($ips != "") ) ) {
+	if( $blocker_block_data == null ) {
+		// no data for given blocker, aborting...
+		wfProfileOut( __METHOD__ );
+		return false;
+	}
+
+	$ips = isset($blocker_block_data['ips']) ? $blocker_block_data['ips'] : null;
+	$names = isset($blocker_block_data['regex']) ? $blocker_block_data['regex'] : null;
+	$exact = isset($blocker_block_data['exact']) ? $blocker_block_data['exact'] : null;
+	// backward compatibility ;)
+	$result = $blocker_block_data;
+
+	/* check IPs */
+	if ( (!empty($ips)) && (in_array($user_ip, $ips)) ) {
+		$result['ips']['matches'] = array($user_ip);
+		wfDebugLog('RegexBlock', "Found some IPs to block: ". implode(",", $result['ips']['matches']). "\n");
+	}
+
+	/* check regexes */
+	if ( ( !empty( $result['regex'] ) ) && ( is_array( $result['regex'] ) ) ) {
+		$result['regex']['matches'] = wfRegexBlockPerformMatch( $result['regex'], $user->getName() );
+		if( !empty( $result['regex']['matches'] ) ) {
+			wfDebugLog('RegexBlock', "Found some regexes to block: ". implode(",", $result['regex']['matches']). "\n");
+		}
+	}
+
+	/* check names of user */
+	$exact = ( is_array( $exact ) ) ? $exact : array($exact);
+	if ( ( !empty( $exact ) ) && ( in_array( $user->getName(), $exact ) ) ) {
+		$key = array_search( $user->getName(), $exact );
+		$result['exact']['matches'] = array($exact[$key]);
+		wfDebugLog('RegexBlock', "Found some users to block: ". implode(",", $result['exact']['matches']). "\n");
+	}
+
+	unset($ips);
+	unset($names);
+	unset($exact);
+
+	/**
+	 * Run expire checks for all matched values
+	 * this is only for determining validity of this block, so
+	 * a first successful match means the block is applied
+	 */
+	$valid = false;
+	foreach( $result as $key => $value ) {
+		$is_ip = ("ips" == $key) ? 1 : 0;
+		$is_regex = ("regex" == $key) ? 1 : 0;
 		/* check if this block hasn't expired already  */
-		if ($matched_ip && ($ips != "")) {	
-			$valid = wfRegexBlockExpireCheck ($user, null, $ip_matches);
-		} else {
-			$valid = wfRegexBlockExpireCheck ($user, $matches, null);
+		if ( !empty( $result[$key]['matches'] ) ) {
+			$valid = wfRegexBlockExpireCheck( $user, $result[$key]['matches'], $is_ip, $is_regex );
+			if ( is_array( $valid ) ) {
+				break;
+			}
 		}
-		if ( is_array ($valid) ) {
-			$user->mBlockedby = $blocker ;
-			if ($valid['reason'] != "") { /* a reason was given, display it */
-				$user->mBlockreason = $valid['reason'];
-			} else { /* display generic reasons */
-				if ($matched_ip && ($ips != "") ) { /* we blocked by IP */
-					$user->mBlockreason = wfMsgHtml('regexblock-reason-ip', $wgContactLink);
-				} else if ($valid['exact'] == 1) { /* we blocked by username exact match */
-					$user->mBlockreason =  wfMsgHtml('regexblock-reason-name', $wgContactLink);
-				} else { /* we blocked by regex match */
-					$user->mBlockreason =  wfMsgHtml('regexblock-reason-regex', $wgContactLink);
-				}
+	}
+
+	if ( is_array( $valid ) ) {
+		wfRegexBlockSetUserData( $user, $user_ip, $blocker, $valid );
+	}
+
+	wfProfileOut( __METHOD__ );
+	return true;
+}
+
+/**
+ * Update user structure
+ *
+ * @param $user User
+ * @param $user_ip String
+ * @param $blocker String
+ * @param $valid: blocked info
+ */
+function wfRegexBlockSetUserData( &$user, $user_ip, $blocker, $valid ) {
+	global $wgContactLink;
+	wfProfileIn( __METHOD__ );
+	$result = false;
+
+	if ( !( $user instanceof User ) ) {
+		wfProfileOut( __METHOD__ );
+		return $result;
+	}
+
+	wfLoadExtensionMessages( 'RegexBlock' );
+
+	if ( empty( $wgContactLink ) ) {
+		$wgContactLink = '[[Special:Contact|contact us]]';
+	}
+
+	if ( is_array( $valid ) ) {
+		$user->mBlockedby = User::idFromName($blocker);
+		if ( $valid['reason'] != '' ) {
+			/* a reason was given, display it */
+			$user->mBlockreason = $valid['reason'];
+		} else { 
+			/**
+			 * Display generic reasons
+			 * By default we blocked by regex match
+			 */
+			$user->mBlockreason = wfMsg( 'regexblock-reason-regex', $wgContactLink );
+			if ( $valid['ip'] == 1 ) {
+				/* we blocked by IP */
+				$user->mBlockreason = wfMsg( 'regexblock-reason-ip', $wgContactLink );
+			} else if ($valid['exact'] == 1) { 
+				/* we blocked by username exact match */
+				$user->mBlockreason = wfMsg( 'regexblock-reason-name', $wgContactLink );
 			}
-			/* account creation check goes through the same hook... */			
-			if ($valid['create'] == 1) $user->mBlock->mCreateAccount = 1;
+		}
+		/* account creation check goes through the same hook... */
+		if ( $valid['create'] == 1 ) {
+			if ( $user->mBlock ) {
+				$user->mBlock->mCreateAccount = 1;
+			}
+		}
+		/* set expiry information */
+		if ( $user->mBlock ) {
+			$user->mBlock->mId = $valid['blckid'];
+			$user->mBlock->mExpiry = $valid['expire']; 
+			$user->mBlock->mTimestamp = $valid['timestamp'];
+			$user->mBlock->mAddress = ($valid['ip'] == 1) ? wfGetIP() : $user->getName();
+		}
 
-			wfRegexBlockUpdateStats ($username, $user_ip, $blocker);
-		}
+		$result = wfRegexBlockUpdateStats( $user, $user_ip, $blocker, $valid['match'], $valid['blckid'] );
 	}
+
+	wfProfileOut( __METHOD__ );
+	return $result;
 }
+
+/**
+ * Clean the memcached keys
+ *
+ * @param $username name of username
+ */
+function wfRegexBlockUnsetKeys( $username ) {
+	global $wgSharedDB, $wgUser;
+	wfProfileIn( __METHOD__ );
+
+	$readMaster = 1;
+	$oMemc = wfGetCache( CACHE_MEMCACHED );
+	$key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_SPECIAL_KEY, REGEXBLOCK_SPECIAL_NUM_RECORD );
+	$oMemc->delete( $key );
+	/* main cache of user-block data */
+	$key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_USER_KEY, str_replace( ' ', '_', $username ) );
+	$oMemc->delete( $key );
+	/* blockers */
+	$key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_BLOCKERS_KEY );
+	$oMemc->delete( $key );
+	$blockers_array = wfRegexBlockGetBlockers( $readMaster );
+	/* blocker's matches */
+	$key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_BLOCKERS_KEY, "All-In-One" );
+	$oMemc->delete( $key );
+	wfGetRegexBlockedData( $wgUser, $blockers_array, $readMaster );
+
+	wfProfileOut( __METHOD__ );
+}
\ No newline at end of file
Index: trunk/extensions/regexBlock/regexBlock.i18n.php
===================================================================
--- trunk/extensions/regexBlock/regexBlock.i18n.php	(revision 45427)
+++ trunk/extensions/regexBlock/regexBlock.i18n.php	(revision 45428)
@@ -1,94 +1,77 @@
 <?php
 /**
- * Internationalisation file for extension regexBlock.
+ * Internationalisation file for regexBlock extension.
  *
- * @addtogroup Extensions
-*/
+ * @file
+ * @ingroup Extensions
+ */
 
 $messages = array();
 
+/** English
+ * @author Bartek Łapiński
+ * @author Piotr Molski
+ * @author Tomasz Klim
+ */
 $messages['en'] = array(
-	'regexblock'                      => 'Regex block',
-	'regexblock-desc'                 => 'Extension used for blocking users names and IP addresses with regular expressions. Contains both the blocking mechanism and a [[Special:Regexblock|special page]] to add/manage blocks',
-	'regexblock-special-desc'         => 'alternate user block (by given name, using regular expressions)',
-	'regexblock-stat-desc'            => 'Displays [[Special:Regexblockstats|block statistics]] for the regexblock extension',
-	'regexblock-page-title'           => 'Regular expression name block',
-	'regexblockstats'                 => 'Regex block statistics',
-	'regexblock-reason-ip'            => 'This IP address is prevented from editing due to vandalism or other disruption by you or by someone who shares your IP address.
-If you believe this is in error, please $1' ,
-	'regexblock-reason-name'          => 'This username is prevented from editing due to vandalism or other disruption.
-If you believe this is in error, please $1',
-	'regexblock-reason-regex'         => 'This username is prevented from editing due to vandalism or other disruption by a user with a similar name.
-Please create an alternate user name or $1 about the problem',
-	'regexblock-help'                 => 'Use the form below to block write access from a specific IP address or username.
+	'regexblock' => 'Regex block',
+	'regexblock-already-blocked' => '$1 is already blocked.',
+	'regexblock-block-log' => 'User name or IP address \'\'\'$1\'\'\' has been blocked.',
+	'regexblock-block-success' => 'Block succedeed',
+	'regexblock-currently-blocked' => 'Currently blocked addresses:',
+	'regexblock-desc' => 'Extension used for blocking users names and IP addresses with regular expressions. Contains both the blocking mechanism and a [[Special:Regexblock|special page]] to add/manage blocks',
+	'regexblock-expire-duration' => '1 hour,2 hours,4 hours,6 hours,1 day,3 days,1 week,2 weeks,1 month,3 months,6 months,1 year,infinite',
+	'regexblock-page-title' => 'Regular expression name block',
+	'regexblockstats' => 'Regex block statistics',
+	'regexblock-help' => 'Use the form below to block write access from a specific IP address or username.
 This should be done only to prevent vandalism, and in accordance with policy.
 \'\'This page will allow you to block even non-existing users, and will also block users with names similar to given, i.e. "Test" will be blocked along with "Test 2" etc.
 You can also block full IP addresses, meaning that no one logging in from them will be able to edit pages.
 Note: partial IP addresses will be treated by usernames in determining blocking.
 If no reason is specified, a default generic reason will be used.\'\'',
-	'regexblock-page-title-1'         => 'Block address using regular expressions',
-	'regexblock-unblock-success'      => 'Unblock succeeded',
-	'regexblock-unblock-log'          => 'User name or IP address \'\'\'$1\'\'\' has been unblocked.',
-	'regexblock-unblock-error'        => 'Error unblocking $1.
+	'regexblock-page-title-1' => 'Block address using regular expressions',
+	'regexblock-reason-ip' => 'This IP address is prevented from editing due to vandalism or other disruption by you or by someone who shares your IP address.
+If you believe this is in error, please $1' ,
+	'regexblock-reason-name' => 'This username is prevented from editing due to vandalism or other disruption.
+If you believe this is in error, please $1',
+	'regexblock-reason-regex' => 'This username is prevented from editing due to vandalism or other disruption by a user with a similar name.
+Please create an alternate user name or $1 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',
+	'regexblock-form-submit-empty' => 'Give a user name or an IP address to block.',
+	'regexblock-form-submit-regex' => 'Invalid regular expression.',
+	'regexblock-form-submit-expiry' => 'Please specify an expiration period.',
+	'regexblock-match-stats-record' => "$1 blocked '$2' on '$3' at '$4', logging from address '$5'",
+	'regexblock-nodata-found' => 'No data found',
+	'regexblock-stats-title' => 'Regex block statistics',
+	'regexblock-unblock-success' => 'Unblock succeeded',
+	'regexblock-unblock-log' => 'User name or IP address \'\'\'$1\'\'\' has been unblocked.',
+	'regexblock-unblock-error' => 'Error unblocking $1.
 Probably there is no such user.',
-	'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',
-	'regexblock-block-log'            => 'User name or IP address \'\'\'$1\'\'\' has been blocked.',
-	'regexblock-block-success'        => 'Block succedeed',
-	'regexblock-form-submit-empty'    => 'Give a user name or an IP address to block.',
-	'regexblock-form-submit-regex'    => 'Invalid regular expression.',
-	'regexblock-form-submit-expiry'   => 'Please specify an expiration period.',
-	'regexblock-already-blocked'      => '$1 is already blocked.',
-	'regexblock-stats-title'          => 'Regex block statistics',
-	'regexblock-stats-username'       => 'For $1',
-	'regexblock-stats-times'          => 'was blocked on',
-	'regexblock-stats-logging'        => 'logging from address',
-	'regexblock-currently-blocked'    => 'Currently blocked addresses:',
-	'regexblock-view-blocked'         => 'View blocked by:',
-	'regexblock-view-all'             => 'All',
-	'regexblock-view-go'              => 'Go',
-	'regexblock-view-match'           => '(exact match)',
-	'regexblock-view-regex'           => '(regex match)',
-	'regexblock-view-account'         => '(account creation block)',
-	'regexblock-view-reason'          => 'reason: $1',
-	'regexblock-view-reason-default'  => 'generic reason',
-	'regexblock-view-block-infinite'  => 'permanent block',
+	'regexblock-regex-filter' => ' or regex value: ',
+	'regexblock-view-blocked' => 'View blocked by:',
+	'regexblock-view-all' => 'All',
+	'regexblock-view-go' => 'Go',
+	'regexblock-view-match' => '(exact match)',
+	'regexblock-view-regex' => '(regex match)',
+	'regexblock-view-account' => '(account creation block)',
+	'regexblock-view-reason' => 'reason: $1',
+	'regexblock-view-reason-default' => 'generic reason',
+	'regexblock-view-block-infinite' => 'permanent block',
 	'regexblock-view-block-temporary' => 'expires on ',
-	'regexblock-view-block-expired'   => 'EXPIRED on ',
-	'regexblock-view-block-by'        => 'blocked by ',
-	'regexblock-view-block-unblock'   => 'unblock',
-	'regexblock-view-stats'           => '(stats)',
-	'regexblock-view-empty'           => 'The list of blocked names and addresses is empty.',
-	'regexblock-view-time'            => 'on $1',
+	'regexblock-view-block-expired' => 'EXPIRED on ',
+	'regexblock-view-block-by' => 'blocked by ',
+	'regexblock-view-block-unblock' => 'unblock',
+	'regexblock-view-stats' => 'stats',
+	'regexblock-view-empty' => 'The list of blocked names and addresses is empty.',
+	'regexblock-view-time' => 'on $1',
+	'right-regexblock' => 'Block users from editing on all wikis on the wiki farm',
 );
 
-/** Message documentation (Message documentation)
- * @author Jon Harald Søby
- * @author Purodha
- * @author SPQRobin
- */
-$messages['qqq'] = array(
-	'regexblock-desc' => 'Short description of this extension, shown in [[Special:Version]]. Do not translate or change links.',
-	'regexblock-special-desc' => 'Short description of this extension, shown in [[Special:Version]]. Do not translate or change links.',
-	'regexblock-stat-desc' => 'Short description of this extension, shown in [[Special:Version]]. Do not translate or change links.',
-	'regexblock-reason-ip' => 'Parameter $1 is <tt>$wgContactLink</tt>, which is by default "<tt><nowiki>[[Special:Contact|contact Wikia]]</nowiki></tt>".',
-	'regexblock-reason-name' => 'Parameter $1 is <tt>$wgContactLink</tt>, which is by default "<tt><nowiki>[[Special:Contact|contact Wikia]]</nowiki></tt>".',
-	'regexblock-reason-regex' => 'Parameter $1 is <tt>$wgContactLink</tt>, which is by default "<tt><nowiki>[[Special:Contact|contact Wikia]]</nowiki></tt>".',
-	'regexblock-form-reason' => '{{Identical|Reason}}',
-	'regexblock-form-expiry' => '{{Identical|Expiry}}',
-	'regexblock-form-match' => '{{Identical|Exact match}}',
-	'regexblock-already-blocked' => '{{Identical|$1 is already blocked}}',
-	'regexblock-stats-username' => '{{Identical|For $1}}',
-	'regexblock-view-all' => '{{Identical|All}}',
-	'regexblock-view-go' => '{{Identical|Go}}',
-	'regexblock-view-match' => '{{Identical|Exact match}}',
-	'regexblock-view-block-temporary' => '{{Identical|Expires on}}',
-);
-
 /** Niuean (ko e vagahau Niuē)
  * @author Jose77
  */
@@ -1970,5 +1953,4 @@
 	'regexblock-view-reason-default' => '一般原因',
 	'regexblock-view-block-infinite' => '永久封禁',
 	'regexblock-view-stats' => '(統計)',
-);
-
+);
\ No newline at end of file
Index: trunk/extensions/regexBlock/regexBlock.php
===================================================================
--- trunk/extensions/regexBlock/regexBlock.php	(revision 45427)
+++ trunk/extensions/regexBlock/regexBlock.php	(revision 45428)
@@ -1,11 +1,10 @@
 <?php
-
-/**#@+
+/**
  * Extension used for blocking users names and IP addresses with regular expressions. Contains both the blocking mechanism and a special page to add/manage blocks
  *
- * @addtogroup SpecialPage
- *
- * @author Bartek Łapiński
+ * @file
+ * @ingroup Extensions
+ * @author Bartek Łapiński <bartek at wikia-inc.com>
  * @copyright Copyright © 2007, Wikia Inc.
  * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
  */
@@ -14,63 +13,58 @@
  * Protect against register_globals vulnerabilities.
  * This line must be present before any global variable is referenced.
  */
-if (!defined('MEDIAWIKI')) die();
+if( !defined('MEDIAWIKI') )
+	die();
 
-/* generic reasons */
+/* name of the block table */
+define('REGEXBLOCK_TABLE', 'blockedby');
+/* name of the statistic table */
+define('REGEXBLOCK_STATS_TABLE', 'stats_blockedby');
+define('REGEXBLOCK_MASK', '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/'); 
+/* modes for fetching data during blocking */
+define('REGEXBLOCK_MODE_NAMES', 0);
+define('REGEXBLOCK_MODE_IPS', 1);
+/* for future use */
+define('REGEXBLOCK_USE_STATS', 1);
+/* memcached expiration time (0 - infinite) */
+define('REGEXBLOCK_EXPIRE', 0);
+/* memcached keys */
+define('REGEXBLOCK_USER_KEY', 'regex_user_block');
+define('REGEXBLOCK_BLOCKERS_KEY', 'regex_blockers');
+define('REGEXBLOCK_SPECIAL_KEY', 'regexBlockSpecial');
+define('REGEXBLOCK_SPECIAL_NUM_RECORD', 'number_records');
 
+/* add hook */
+$wgHooks['GetBlockedStatus'][] = 'wfRegexBlockCheck';
+
+/* generic reasons */
 global $wgContactLink;
 
-if($wgContactLink == ''){
+if( $wgContactLink == '' ){
 	$wgContactLink = '[[Special:Contact|contact us]]';
 }
 
+// New user right
+$wgAvailableRights[] = 'regexblock';
+$wgGroupPermissions['staff']['regexblock'] = true;
+
+// Extension credits that will show up on Special:Version
 $wgExtensionCredits['specialpage'][] = array(
 	'name' => 'regexBlock',
-	'author' => 'Bartek Łapiński',
+	'author' => array( 'Bartek Łapiński', 'Tomasz Klim', 'Piotr Molski', 'Adrian Wieczorek' ),
 	'url' => 'http://www.mediawiki.org/wiki/Extension:RegexBlock',
-	'svn-date' => '$LastChangedDate$',
-	'svn-revision' => '$LastChangedRevision$',
+	'version' => '1.2',
 	'description' => 'Extension used for blocking users names and IP addresses with regular expressions. Contains both the blocking mechanism and a special page to add/manage blocks.',
 	'descriptionmsg' => 'regexblock-desc',
 );
 
-$dir = dirname(__FILE__) . '/';
+// Set up the new special page
+$dir = dirname( __FILE__ ) . '/';
 $wgExtensionMessagesFiles['RegexBlock'] = $dir . 'regexBlock.i18n.php';
+$wgAutoloadClasses['RegexBlockForm'] = $dir . 'SpecialRegexBlock.php';
+$wgSpecialPages['RegexBlock'] = 'RegexBlockForm';
+// Special page group for MW 1.13+
+$wgSpecialPageGroups['RegexBlock'] = 'users';
 
-define ('REGEXBLOCK_PATH', '/') ;
-
-/* get name of the table  */
-function wfRegexBlockGetTable () {
-	global $wgSharedDB;
-	if ("" != $wgSharedDB) {
-		return "{$wgSharedDB}.blockedby";
-	} else {
-		return 'blockedby';
-	}
-}
-
-/* get the name of the stats table */
-function wfRegexBlockGetStatsTable () {
-	global $wgSharedDB;
-	if ("" != $wgSharedDB) {
-		return "{$wgSharedDB}.stats_blockedby";
-	} else {
-		return 'stats_blockedby';
-	}
-}
-
-/* memcached expiration time (0 - infinite) */
-define ('REGEXBLOCK_EXPIRE', 0);
-/* modes for fetching data during blocking */
-define ('REGEXBLOCK_MODE_NAMES', 0);
-define ('REGEXBLOCK_MODE_IPS', 1);
-/* for future use */
-define ('REGEXBLOCK_USE_STATS', 1);
-
 /* core includes */
-require_once ($IP.REGEXBLOCK_PATH."extensions/regexBlock/regexBlockCore.php");
-require_once ($IP.REGEXBLOCK_PATH."extensions/regexBlock/SpecialRegexBlock.php");
-require_once ($IP.REGEXBLOCK_PATH."extensions/regexBlock/SpecialRegexBlockStats.php");
-
-/* simplified regexes, this is shared with SpamRegex */
-require_once ($IP.REGEXBLOCK_PATH."extensions/SimplifiedRegex/SimplifiedRegex.php");
+require_once("$IP/extensions/regexBlock/regexBlockCore.php");
\ No newline at end of file

Property changes on: trunk/extensions/regexBlock/regexBlock.php
___________________________________________________________________
Name: svn:keywords
   - LastChangedDate LastChangedRevision

Index: trunk/extensions/regexBlock/SpecialRegexBlock.php
===================================================================
--- trunk/extensions/regexBlock/SpecialRegexBlock.php	(revision 45427)
+++ trunk/extensions/regexBlock/SpecialRegexBlock.php	(revision 45428)
@@ -1,465 +1,739 @@
 <?php
-
-/**#@+
+/**
  * A special page with the interface for blocking, viewing and unblocking
  * user names and IP addresses
  *
- * @addtogroup SpecialPage
- *
- * @author Bartek Łapiński
+ * @file
+ * @ingroup Extensions
+ * @author Bartek Łapiński <bartek at wikia-inc.com>, Piotr Molski <moli at wikia-inc.com>
  * @copyright Copyright © 2007, Wikia Inc.
  * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 */
 
-if(!defined('MEDIAWIKI'))
-   die();
+/**
+ * Protect against register_globals vulnerabilities.
+ * This line must be present before any global variable is referenced.
+ */
+if( !defined('MEDIAWIKI') )
+	die();
 
-$wgAvailableRights[] = 'regexblock';
-$wgGroupPermissions['staff']['regexblock'] = true;
+class RegexBlockForm extends SpecialPage {
+	var $mRegexUnblockedAddress;
+	var $numResults = 0;
+	var $numStatResults = 0;
+	var $mPosted, $mAction;
+	var $mFilter, $mRegexFilter;
+	var $mLimit;
+	var $mOffset;
+	var $mError, $mMsg;
 
-$wgExtensionFunctions[] = 'wfRegexBlockSetup';
-$wgExtensionCredits['specialpage'][] = array(
-	'name' => 'Regular Expression Name Block',
-	'author' => 'Bartek Łapiński',
-	'url' => 'http://www.mediawiki.org/wiki/Extension:RegexBlock',
-	'description' => 'alternate user block (by given name, using regular expressions)',
-	'descriptionmsg' => 'regexblock-special-desc',
-);
+	/**
+	 * Constructor
+	 */
+	public function __construct() {
+		$this->mPosted = false;
+		$this->mAction = '';
+		$this->mFilter = $this->mRegexFilter = '';
+		$this->mError = $this->mMsg = '';
+		parent::__construct( 'RegexBlock'/*class*/, 'regexblock'/*restriction*/ );
+		wfLoadExtensionMessages('RegexBlock');
+	}
 
-/* special page init */
-$wgSpecialPageGroups['Regexblock'] = 'users';
-function wfRegexBlockSetup() {
-	global $IP;
-	if (!wfSimplifiedRegexCheckSharedDB())
-		return;
-	require_once($IP. '/includes/SpecialPage.php');
-	SpecialPage::addPage(new SpecialPage('Regexblock', 'regexblock', true, 'wfRegexBlockSpecial', false));
-	wfLoadExtensionMessages( 'RegexBlock' );
-}
+	/**
+	 * Show the special page
+	 *
+	 * @param $subpage Mixed: parameter passed to the page or null
+	 */
+	public function execute( $subpage ) {
+		global $wgUser, $wgOut, $wgRequest;
 
-/* wrapper for GET values */
-function wfGetListBits () {
-	global $wgRequest;
-	$pieces = array();
-	list( $limit, $offset ) = $wgRequest->getLimitOffset();
-	$pieces[] = 'limit=' . $limit;
-	$pieces[] = 'offset=' . $offset;
-	$pieces[] = 'filter=' . urlencode ($wgRequest->getVal ('filter') );
-	$bits = implode( '&', $pieces );
-	return $bits;
-}
+		# If the user doesn't have the required 'regexblock' permission, display an error
+		if ( !$wgUser->isAllowed( 'regexblock' ) ) {
+			$this->displayRestrictionError();
+			return;
+		}
 
-/* draws one option for select */
-function wfRegexBlockMakeOption ($name, $value, $current) {
-	global $wgOut;
-	if ($value == $current) {
-		$wgOut->addHTML ("<option selected=\"selected\" value=\"{$value}\">{$name}</option>");
-	} else {
-		$wgOut->addHTML ("<option value=\"{$value}\">{$name}</option>");
-	}
-}
+		# Show a message if the database is in read-only mode
+		if ( wfReadOnly() ) {
+			$wgOut->readOnlyPage();
+			return;
+		}
 
-/* the core */
-function wfRegexBlockSpecial( $par ) {
-	global $wgOut, $wgUser, $wgRequest;
-	$wgOut->setPageTitle(wfMsg('regexblock-page-title'));
-	$rBS = new regexBlockForm($par);
-	$rBL = new regexBlockList($par);
+		# If user is blocked, s/he doesn't need to access this page
+		if ( $wgUser->isBlocked() ) {
+			$wgOut->blockedPage();
+			return;
+		}
 
-	$action = $wgRequest->getVal( 'action' );
-	if ( 'success_block' == $action ) {
-		$rBS->showSuccess();
-		$rBS->showForm('');
-	} else if ( 'success_unblock' == $action ) {
-		$rBL->showSuccess();
-		$rBS->showForm('');
-	} else if ( 'failure_unblock' == $action ) {
-		$ip = $wgRequest->getVal('ip');
-		$rBS->showForm (wfMsgHtml('regexblock-unblock-error', $ip));
-	} else if ( $wgRequest->wasPosted() && 'submit' == $action &&
-		$wgUser->matchEditToken( $wgRequest->getVal ('wpEditToken') ) ) {
-	$rBS->doSubmit();
-	} else if ('delete' == $action) {
-		$rBL->deleteFromRegexBlockList();
-	} else {
-		$rBS->showForm('');
-	}
-		$rBL->showList('', $offset );
-}
+		// Initial output
+		$this->mTitle = Title::makeTitle( NS_SPECIAL, 'RegexBlock' );
+		$wgOut->setRobotPolicy( 'noindex,nofollow' );
+		$wgOut->setPageTitle( wfMsg('regexblock-page-title') );
+		$wgOut->setArticleRelated( false );
 
-/* useful for cleaning the memcached keys */
-function wfRegexBlockUnsetKeys ($blocker, $username) {
-	global $wgMemc, $wgSharedDB;
-	$wgMemc->delete ("$wgSharedDB:regexBlockSpecial:numResults");
-	$wgMemc->delete ( str_replace( " ", "_", "$wgSharedDB:regexBlockCore:".REGEXBLOCK_MODE_NAMES.":blocker:$blocker") );
-	$wgMemc->delete ( str_replace( " ", "_", "$wgSharedDB:regexBlockCore:".REGEXBLOCK_MODE_IPS.":blocker:$blocker") );
-	$wgMemc->delete ("$wgSharedDB:regexBlockCore:blockers");
-	$wgMemc->delete ( str_replace( " ", "_", "$wgSharedDB:regexBlockCore:blocked:$username") );
-}
+		$this->mPosted = $wgRequest->wasPosted();
+		$this->mAction = $wgRequest->getVal( 'action' );
+		$this->mFilter = $wgRequest->getVal( 'filter' );
+		$this->mRegexFilter = $wgRequest->getVal( 'rfilter' );
+		
+		list( $this->mLimit, $this->mOffset ) = $wgRequest->getLimitOffset();
 
-/* the list of blocked names/addresses */
-class regexBlockList {
-	var $mRegexUnblockedAddress;
-	var $numResults = 0;
+		$this->mRegexBlockedAddress = $this->mRegexBlockedExact = $this->mRegexBlockedCreation = $this->mRegexBlockedExpire = $this->mRegexBlockedReason = '';
+		if ( $this->mAction == 'submit' ) {
+			$this->mRegexBlockedAddress = htmlspecialchars( $wgRequest->getVal( 'wpRegexBlockedAddress', $wgRequest->getVal( 'ip' ) ) );
+			$this->mRegexBlockedExact = $wgRequest->getInt( 'wpRegexBlockedExact' );
+			$this->mRegexBlockedCreation = $wgRequest->getInt( 'wpRegexBlockedCreation' );
+			$this->mRegexBlockedExpire = htmlspecialchars( $wgRequest->getVal( 'wpRegexBlockedExpire' ) );
+			$this->mRegexBlockedReason = htmlspecialchars( $wgRequest->getVal( 'wpRegexBlockedReason' ) );
+		}
 
-	/* constructor */
-	function regexBlockList ( $par ) {
+		/* Actions */
+		switch( $this->mAction ) {
+			case 'success_block':
+				$wgOut->setSubTitle( wfMsg( 'regexblock-block-success' ) );
+				$this->mMsg = wfMsgWikiHtml( 'regexblock-block-log', array( htmlspecialchars( $wgRequest->getVal( 'ip' ) ) ) );
+				break;
+			case 'success_unblock':
+				$wgOut->setSubTitle( wfMsg( 'regexblock-unblock-success' ) );
+				$this->mMsg = wfMsgWikiHtml( 'regexblock-unblock-log', array( htmlspecialchars( $wgRequest->getVal( 'ip' ) ) ) );
+				break;
+			case 'failure_unblock': 
+				$this->mError = wfMsgWikiHtml( 'regexblock-unblock-error', array( htmlspecialchars( $wgRequest->getVal( 'ip' ) ) ) );
+				break;
+			case 'stats':
+				$blckid = $wgRequest->getVal( 'blckid' );
+				$this->showStatsList($blckid);
+				break;	
+			case 'submit':			
+   				if ( $wgRequest->wasPosted() && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+   					$this->mAction = $this->doSubmit();
+				}
+				break;
+			case 'delete': 
+				$this->deleteFromRegexBlockList();
+				break;
+		}
+
+		if ( !in_array( $this->mAction, array( 'submit', 'stats' ) ) ) {
+			$this->showForm();
+			unset($this->mError);
+			unset($this->mMsg);
+			$this->showRegexList();
+		}
 	}
 
-	/* output list */
-	function showList ( $err ) {
-		global $wgOut, $wgRequest, $wgMemc, $wgLang, $wgUser;
+	/**
+	 * Show the form for blocking IPs / users
+	 */
+	private function showForm() {
+		global $wgOut, $wgUser, $wgRequest;
+		wfProfileIn( __METHOD__ );
+   
+		$token = htmlspecialchars( $wgUser->editToken() );
+		$titleObj = Title::makeTitle( NS_SPECIAL, 'RegexBlock' );
+		$action = $titleObj->escapeLocalURL( "action=submit" )."&".$this->makeListUrlParams();
+		$err = $this->mError;
+		$msg = $this->mMsg;
 
-		/* on error, display error */
-		if ( "" != $err ) {
-			$wgOut->addHTML ("<p class='error'>{$err}</p>\n");
+		$expiries = RegexBlockData::getExpireValues();
+		$regexBlockAddress = ( empty( $this->mRegexBlockedAddress ) && ( $wgRequest->getVal( 'ip' ) != null ) &&
+			( $wgRequest->getVal( 'action' ) == null ) ) ? $wgRequest->getVal( 'ip' ) : $this->mRegexBlockedAddress;
+
+		$wgOut->addHTML('<div style="float:left; clear:both; margin-left: auto; margin-right:auto">');
+		if ( '' != $err ) {
+			$wgOut->setSubtitle( wfMsgHtml( 'formerror' ) );
+			$wgOut->addHTML('<h2 class="errorbox">'.$this->mError.'</h2>');
+		} elseif ( $msg != '' ) {
+			$wgOut->addHTML('<h2 class="successbox">'.$this->mMsg.'</h2>');
 		}
-		$titleObj = Title::makeTitle( NS_SPECIAL, 'Regexblock' );
-		$action = $titleObj->escapeLocalURL("") ."?".wfGetListBits();
-		$action_unblock = $titleObj->escapeLocalURL("action=delete") ."&".wfGetListBits();
-		list( $limit, $offset ) = $wgRequest->getLimitOffset();
+		$wgOut->addHTML('</div><div style="clear:both; width:auto;">'.wfMsgExt('regexblock-help', 'parse').'</div>
+		<fieldset style="width:90%; margin:auto;" align="center"><legend>'.wfMsg('regexblock-form-submit').'</legend>
+		<form name="regexblock" method="post" action="'.$action.'">
+		<table border="0">
+		<tr>
+		<td align="right">'.wfMsg('regexblock-form-username').'</td>
+		<td align="left">
+			<input tabindex="1" name="wpRegexBlockedAddress" id="wpRegexBlockedAddress" size="40" value="'.$regexBlockAddress.'" style="border: 1px solid #2F6FAB;" />
+		</td>
+		</tr>
+		<tr>
+		<td align="right">'.ucfirst( wfMsg('regexblock-form-reason') ).'</td>
+		<td align="left">
+			<input tabindex="2" name="wpRegexBlockedReason" id="wpRegexBlockedReason" size="40" value="'.$this->mRegexBlockedReason.'" style="border: 1px solid #2F6FAB;" />
+		</td>
+		</tr>
+		<tr>
+		<td align="right">'.ucfirst( wfMsg('regexblock-form-expiry') ).'</td>
+		<td align="left">
+			<select name="wpRegexBlockedExpire" id="wpRegexBlockedExpire" tabindex="3" style="border: 1px solid #2F6FAB;">'."\n");
+		foreach( $expiries as $k => $v ) {
+			$selected = htmlspecialchars( ($k == $this->mRegexBlockedExpire) ) ? ' selected="selected"' : '';
+			$wgOut->addHTML('<option value="'.htmlspecialchars($v).'"'.$selected.'>'.htmlspecialchars($v).'</option>');
+		}
+		$wgOut->addHTML('</select>');
+		$checkExact = htmlspecialchars( ($this->mRegexBlockedExact) ) ? 'checked="checked"' : '';
+		$checkCreation = htmlspecialchars( ($this->mRegexBlockedCreation) ) ? 'checked="checked"' : '';
+		$wgOut->addHTML('</td></tr>
+						<tr>
+						<td align="right">&nbsp;</td>
+						<td align="left">
+							<input type="checkbox" tabindex="4" name="wpRegexBlockedExact" id="wpRegexBlockedExact" value="1" '.$checkExact.' />
+							<label for="wpRegexBlockedExact">'. ucfirst( wfMsg('regexblock-form-match') ) .'</label>
+						</td></tr>
+						<tr>
+						<td align="right">&nbsp;</td>
+						<td align="left">
+							<input type="checkbox" tabindex="5" name="wpRegexBlockedCreation" id="wpRegexBlockedCreation" value="1" '.$checkCreation.' />
+							<label for="wpRegexBlockedCreation">'.wfMsg('regexblock-form-account-block').'</label>
+						</td></tr>
+						<tr>
+						<td align="right">&nbsp;</td>
+						<td align="left">
+							<input tabindex="6" name="wpRegexBlockedSubmit" type="submit" value="'.wfMsg('regexblock-form-submit').'" style="color:#2F6FAB;" />
+						</td></tr></table>
+						<input type="hidden" name="wpEditToken" value="'.$token.'" />
+						</form>
+						</fieldset>
+						<br />');
+		wfProfileOut( __METHOD__ );
+	}
 
-		$wgOut->addWikiText ("<br />'''".wfMsg('regexblock-currently-blocked')."'''");
-		$this->fetchNumResults();
-		$this->showPrevNext($wgOut);
+	/**
+	 * Show the list of regex blocks - current and expired, along with some controls (unblock, statistics, etc.)
+	 */
+	private function showRegexList() {
+		global $wgOut, $wgRequest, $wgMemc, $wgLang, $wgUser, $wgContLang;
 
-		$wgOut->addHTML ("<form name=\"regexlist\" method=\"get\" action=\"{$action}\">");
+		wfProfileIn( __METHOD__ );
 
-		/* allow display by specific blockers only */
-		$wgOut->addHTML (wfMsg('regexblock-view-blocked')." <select name=\"filter\"><option value=\"\">".wfMsg('regexblock-view-all')."</option>") ;
-		$blockers =  $this->fetchBlockers();
-		$wgOut->addHTML("</select>&#160;<input type=\"submit\" value=".wfMsg('regexblock-view-go')."><br /><br />
-		");
-		$current = $wgRequest->getVal('filter');
+		$titleObj = Title::makeTitle( NS_SPECIAL, 'RegexBlock' );
+		$action = $titleObj->escapeLocalURL("") ."?".$this->makeListUrlParams();
+		$action_unblock = $titleObj->escapeLocalURL("action=delete") ."&".$this->makeListUrlParams();
 
-		if ($blockers) {
-			/* get data and play with data */
-			$dbr = &wfGetDB (DB_SLAVE);
-			$query = "SELECT * FROM ".wfRegexBlockGetTable();
-			if ('' != $current) {
-				$query .= " WHERE blckby_blocker = {$dbr->addQuotes($current)}";
-			}
-			/* righto, order by name */
-			$query .= " order by blckby_timestamp DESC";
+		$regexData = new RegexBlockData();
+		$this->numResults = $regexData->fetchNbrResults();
+		$filter = 'filter=' . urlencode($this->mFilter) . '&rfilter=' . urlencode($this->mRegexFilter);
+		$pager = wfViewPrevNext( $this->mOffset, $this->mLimit, $wgContLang->specialpage( 'RegexBlock' ), $filter, ($this->numResults - $this->mOffset) <= $this->mLimit );
 
-			$query = $dbr->limitResult ($query, $limit, $offset);
-			$res = $dbr->query($query);
-			$wgOut->addHTML("<ul>");
+		/* allow display by specific blockers only */
+		$blockers = $regexData->fetchBlockers();
+		$blocker_list = array();
+		if ( !empty( $blockers ) ) {
+			$blocker_list = $regexData->getBlockersData($this->mFilter, $this->mRegexFilter, $this->mLimit, $this->mOffset);
+		}
 
-			/* output a single row*/
-			while ( $row = $dbr->fetchObject( $res ) ) {
-				$ublock_ip = urlencode ($row->blckby_name);
-				$ublock_blocker = urlencode ($row->blckby_blocker);
+		/* make link to statistics */
+		$mSkin = $wgUser->getSkin();
+		$wgOut->addHTML('<br /><b>'.wfMsg('regexblock-currently-blocked').'</b>
+					<p>'.$pager.'</p>
+					<form name="regexlist" method="get" action="'.htmlspecialchars($action).'">
+					'.wfMsg('regexblock-view-blocked').'
+					<select name="filter">
+					<option value="">'.wfMsg('regexblock-view-all').'</option>');
 
-				$row->blckby_exact ? $exact_match = wfMsgHtml('regexblock-view-match') : $exact_match = wfMsgHtml('regexblock-view-regex');
-				$row->blckby_create ? $create_block = wfMsgHtml('regexblock-view-account') : $create_block = '';
-				$row->blckby_reason ? $reason = "<i>".wfMsg('regexblock-view-reason',$row->blckby_reason)."</i>" : $reason = "<i>".wfMsg('regexblock-view-reason-default')."</i>";
+		if ( is_array( $blockers ) ) {
+			foreach( $blockers as $id => $blocker ) {
+				$sel = htmlspecialchars( ( $this->mFilter == $blocker ) ) ? ' selected="selected"' : '';
+				$wgOut->addHTML('<option value="'.htmlspecialchars($blocker).'"'. $sel.'>'.htmlspecialchars($blocker).'</option>');
+			}
+		}
 
-				$time = $wgLang->timeanddate( wfTimestamp( TS_MW, $row->blckby_timestamp ), true );
-
-				/* if this block already expired, show it */
-				$expiry = $row->blckby_expire;
-				if ( (wfTimestampNow () <= $expiry) || ('infinite' == $expiry) ) {
-					$expiry == 'infinite' ? $expires = wfMsg('regexblock-view-block-infinite') : $expires = wfMsg('regexblock-view-block-temporary').$wgLang->timeanddate( wfTimestamp( TS_MW, $expiry ), true );
+		$wgOut->addHTML('</select>&nbsp;'.wfMsg('regexblock-regex-filter').'<input type="text" name="rfilter" id="regex_filter" value="'.$this->mRegexFilter.'" />
+					<input type="submit" value="'.wfMsg('regexblock-view-go').'">
+					</form>
+					<br /><br />');
+		if ( !empty( $blockers ) ) {
+			$wgOut->addHTML('<ul>');
+			$loop = 0;
+			$comma = " <b>&#183;</b> "; // the spaces here are intentional
+			foreach( $blocker_list as $id => $row ) { 
+				$loop++;
+				$color_expire = "%s";
+				if ( 'infinite' == $row['expiry'] ) {
+					$row['expiry'] = wfMsg('regexblock-view-block-infinite');
 				} else {
-					$expires = "<span style=\"color: #ff0000\">".wfMsg('regexblock-view-block-expired')."{$wgLang->timeanddate( wfTimestamp( TS_MW, $expiry ), true )}</span>";
+					if ( wfTimestampNow() > $row['expiry'] ) {
+						$color_expire = "<span style=\"color:#DC143C\">%s</span>";
+					}
+					$row['expiry'] = sprintf($color_expire, $wgLang->timeanddate( wfTimestamp( TS_MW, $row['expiry'] ), true ));
 				}
-				$sk = $wgUser->getSkin();
-				$stats_link = $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Regexblockstats' ), wfMsg('regexblock-view-stats'), 'target=' . urlencode($row->blckby_name));
-				$wgOut->addHTML ("
-					<li><b>{$row->blckby_name} {$exact_match} {$create_block}</b> ".wfMsg('regexblock-view-block-by')."<b>{$row->blckby_blocker}</b>, {$reason}) ".wfMsg('regexblock-view-time', $time) ." (<a href=\"{$action_unblock}&ip={$ublock_ip}&blocker={$ublock_blocker}\">".wfMsg('regexblock-view-block-unblock')."</a>) {$expires} {$stats_link}</li>
-				");
+
+				$exact_match = (($row['exact_match']) ? wfMsg('regexblock-view-match') : wfMsg('regexblock-view-regex'));
+				$create_block = ($row['create_block']) ? wfMsg('regexblock-view-account') : '';
+				$reason = '<i>'.$row['reason'].'</i>';
+				$stats_link = $mSkin->makeKnownLinkObj( $titleObj, wfMsg('regexblock-view-stats'), 'action=stats&blckid=' . urlencode($row['blckid']) . '&' . $urls);
+
+				$wgOut->addHTML('<li style="border-bottom:1px dashed #778899; padding-bottom:2px;font-size:11px">
+					<b><font style="color:#3B7F07; font-size:12px">'.$row['blckby_name'].'</font>'.$comma.$exact_match.$create_block.'</b>'.$comma.' 
+					('.wfMsg('regexblock-view-block-by').': <b>'.$row['blocker'].'</b>, '.$reason.') '.wfMsg('regexblock-view-time', $row['time']).$comma.' 
+					(<a href="'.$action_unblock.'&ip='.$row['ublock_ip'].'&blocker='.$row['ublock_blocker'].'">'.wfMsg('regexblock-view-block-unblock').'</a>) '.$comma.$row['expiry'].$comma.' ('.$stats_link.')
+					</li>');
 			}
+			$wgOut->addHTML('</ul><br /><br /><p>'.$pager.'</p>');
+		} else {
+			$wgOut->addWikiMsg('regexblock-view-empty');
+		}
 
-		$dbr->freeResult($res);
-		$wgOut->addHTML("</ul></form>");
-		} else { /* empty list */
-			$wgOut->addHTML (wfMsg('regexblock-view-empty')."<br /><br />");
+		wfProfileOut( __METHOD__ );
+	}
+
+	private function makeListUrlParams( $no_limit = false ) {
+		global $wgRequest;
+		$pieces = array();
+		if ( !$no_limit ) {
+			$pieces[] = 'limit=' . $this->mLimit;
+			$pieces[] = 'offset=' . $this->mOffset;
 		}
-		$this->showPrevNext($wgOut);
+		$pieces[] = 'filter=' . urlencode( $wgRequest->getVal( 'filter' ) );
+		$pieces[] = 'rfilter=' . urlencode( $wgRequest->getVal( 'rfilter' ) );
+		
+		return implode( '&', $pieces );
 	}
 
-	/* a plain html link wrapper */
-	function produceLink ($url, $link, $text) {
-		return $html_link = ("<a href=\"$url$link\">$text</a>");
+	/* On submit */
+	private function doSubmit() {
+		global $wgOut, $wgUser, $wgMemc;
+
+		wfProfileIn( __METHOD__ );
+
+		/* empty name */
+		if ( strlen($this->mRegexBlockedAddress) == 0 ) {
+			$this->mError = wfMsg('regexblock-form-submit-empty');
+			wfProfileOut( __METHOD__ );
+			return false;
+		}
+
+		/* castrate regexes */
+		if ( RegexBlockData::isValidRegex($this->mRegexBlockedAddress) ) {
+			$this->mError = wfMsg('regexblock-form-submit-regex');
+			wfProfileOut( __METHOD__ );
+			return false;
+		}
+
+		/* check expiry */
+		if ( strlen($this->mRegexBlockedExpire) == 0 ) {
+			$this->mError = wfMsg('regexblock-form-submit-expiry');
+			wfProfileOut( __METHOD__ );
+			return false;
+		}
+
+		if ( $this->mRegexBlockedExpire != 'infinite' ) {
+			$expiry = strtotime( $this->mRegexBlockedExpire );
+			if ( $expiry < 0 || $expiry === false ) {
+				$this->mError = wfMsg( 'ipb_expiry_invalid' );
+				wfProfileOut( __METHOD__ );
+				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 */
+		$uname = $wgUser->getName();
+		wfRegexBlockUnsetKeys($this->mRegexBlockedAddress);
+
+		wfProfileOut( __METHOD__ );
+		
+		/* redirect */
+		$titleObj = Title::makeTitle( NS_SPECIAL, 'RegexBlock' );
+		$wgOut->redirect( $titleObj->getFullURL( 'action=success_block&ip=' .urlencode( $this->mRegexBlockedAddress )."&".$this->makeListUrlParams() ) );
+
+		return;
 	}
 
-	/* remove name or address from list - without confirmation */
-	function deleteFromRegexBlockList () {
+	/**
+	 * Remove name or address from list - without confirmation
+	 */
+	private function deleteFromRegexBlockList() {
 		global $wgOut, $wgRequest, $wgMemc, $wgUser;
-		$ip = $wgRequest->getVal('ip');
-		$blocker = $wgRequest->getVal('blocker');
-		/* delete */
-		$dbw =& wfGetDB( DB_MASTER );
-		$query = "DELETE FROM ".wfRegexBlockGetTable()." WHERE blckby_name = ".$dbw->addQuotes($ip);
-		$dbw->query($query);
-		$titleObj = Title::makeTitle( NS_SPECIAL, 'Regexblock' );
-		if ( $dbw->affectedRows() ) {
-			/* success  */
-			wfRegexBlockUnsetKeys ($blocker, $ip);
-		$wgOut->redirect( $titleObj->getFullURL( 'action=success_unblock&ip='.urlencode($ip).'&'.wfGetListBits() ) );
+
+		wfProfileIn( __METHOD__ );
+
+		$result = false;
+		$ip = $wgRequest->getVal( 'ip' );
+		$blocker = $wgRequest->getVal( 'blocker' );
+		$titleObj = Title::makeTitle( NS_SPECIAL, 'RegexBlock' );
+
+		if ( function_exists( 'wfRegexBlockClearExpired' ) ) {
+			$result = wfRegexBlockClearExpired( $ip, $blocker );
 		} else {
-			$wgOut->redirect( $titleObj->getFullURL( 'action=failure_unblock&ip='.urlencode($ip).'&'.wfGetListBits() ) );
+			/* delete */
+			$dbw = wfGetDB( DB_MASTER );
+
+			$dbw->delete( REGEXBLOCK_TABLE, 
+					array("blckby_name = {$dbw->addQuotes($ip)}"),
+					__METHOD__
+				);
+
+			if ( $dbw->affectedRows() ) {
+				/* success, remember to delete cache key  */
+				wfRegexBlockUnsetKeys( $ip );
+				$result = true;
+			}
 		}
+
+		wfProfileOut( __METHOD__ );
+		if ( $result === true ) {
+			$wgOut->redirect( $titleObj->getFullURL( 'action=success_unblock&ip='.urlencode($ip).'&'.$this->makeListUrlParams() ) );
+		} else {
+			$wgOut->redirect( $titleObj->getFullURL( 'action=failure_unblock&ip='.urlencode($ip).'&'.$this->makeListUrlParams() ) );
+		}
+
+		return;
 	}
 
-	/* fetch names of all blockers and write them into select's options */
-	function fetchBlockers () {
-		global $wgOut, $wgRequest, $wgMemc, $wgSharedDB;
-		/* memcached */
-		$key = "$wgSharedDB:regexBlockCore:blockers";
-		$current = $wgRequest->getVal('filter');
-		$cached = $wgMemc->get($key);
-		$fetched = 0;
-		if (!is_array($cached)) {
-			/* get from database */
-			$blockers_array = array();
-			$dbr =& wfGetDB (DB_SLAVE);
-			$query = "SELECT blckby_blocker FROM ".wfRegexBlockGetTable();
-			$query .= " GROUP BY blckby_blocker";
-			$res = $dbr->query($query);
-			while ( $row = $dbr->fetchObject( $res ) ) {
-				wfRegexBlockmakeOption ($row->blckby_blocker, $row->blckby_blocker, $current);
-				array_push ($blockers_array, $row->blckby_blocker);
+	/**
+	 * Display some statistics when a user clicks stats link (&action=stats)
+	 *
+	 * @param $blckid Int: ID number of the block
+	 */
+	private function showStatsList( $blckid ) {
+		global $wgOut, $wgLang, $wgUser, $wgContLang;
+
+		wfProfileIn( __METHOD__ );
+
+		$titleObj = Title::makeTitle( NS_SPECIAL, 'RegexBlock' );
+		$action = $titleObj->escapeLocalURL("") ."?".$this->makeListUrlParams(true);
+		$skin = $wgUser->getSkin();
+
+		$regexData = new RegexBlockData();
+		$this->numStatResults = $regexData->fetchNbrStatResults($blckid);
+		$filter = 'action=stats&filter=' . urlencode($this->mFilter) . '&blckid=' . urlencode($blckid);
+		$pager = wfViewPrevNext($this->mOffset, $this->mLimit, $wgContLang->specialpage( 'RegexBlock' ), $filter, ($this->numStatResults - $this->mOffset) <= $this->mLimit );
+
+		/* allow display by specific blockers only */
+		$blockInfo = $regexData->getRegexBlockById($blckid);
+		$stats_list = array();
+		if ( !empty( $blockInfo ) && ( is_object( $blockInfo ) ) ) {
+			$stats_list = $regexData->getStatsData($blckid, $this->mLimit, $this->mOffset);
+		}
+
+		$blocker_link = $skin->makeKnownLinkObj( $titleObj, $blockInfo->blckby_blocker, 'filter=' . urlencode($blockInfo->blckby_blocker) );
+		$blockername_link = $skin->makeKnownLinkObj( $titleObj, $blockInfo->blckby_name, 'rfilter=' . urlencode($blockInfo->blckby_name) );
+
+		$wgOut->addHTML('<h5>'.wfMsg('regexblock-stats-title').' <strong> '.$blockername_link.'</strong> ('.wfMsg('regexblock-view-block-by').': <b>'.$blocker_link.'</b>,&nbsp;<i>'.( ($blockInfo->blckby_reason) ? wfMsg('regexblock-form-reason') . $blockInfo->blckby_reason : wfMsg('regexblock-view-reason-default') ).'</i>)</h5><br />');
+		if ( !empty( $stats_list ) ) {
+			$wgOut->addHTML('<p>'.$pager.'</p><br /><ul>');
+			foreach( $stats_list as $id => $row ) {
+				$wgOut->addHTML('<li style="border-bottom:1px dashed #778899; padding-bottom:2px;font-size:11px">
+					'.wfMsg('regexblock-match-stats-record', array($row->stats_match, $row->stats_user, htmlspecialchars($row->stats_dbname), $wgLang->timeanddate( wfTimestamp( TS_MW, $row->stats_timestamp ), true ), $row->stats_ip) ).'
+					</li>');
 			}
-			$fetched = $dbr->numRows($res);
-			$dbr->freeResult($res);
-			$wgMemc->set ($key, $blockers_array);
+			$wgOut->addHTML('</ul><br /><p>'.$pager.'</p>');
 		} else {
-			/* get from memcached */
-			foreach ($cached as $blocker) {
-				wfRegexBlockmakeOption ($blocker, $blocker, $current);
-				$fetched++;
-			}
+			$wgOut->addWikiMsg('regexblock-nodata-found');
 		}
-			return $fetched;
+
+		wfProfileOut( __METHOD__ );
 	}
+}
 
-	/* fetch number of all rows */
-	function fetchNumResults () {
+/**
+ * @class RegexBlockData
+ * helper classes & functions
+ * @author Bartek Łapiński
+ * @author Piotr Molski
+ */
+class RegexBlockData {
+	var $mNbrResults;
+
+	public function __construct() {
+		$this->mNbrResults = 0;
+	}	
+
+	/**
+	 * Fetch number of all rows 
+	 */
+	public function fetchNbrResults() {
 		global $wgMemc, $wgSharedDB;
 
+		wfProfileIn( __METHOD__ );
+
+		$this->mNbrResults = 0;
 		/* we use memcached here */
-		$key = "$wgSharedDB:regexBlockSpecial:numResults";
-		$cached = $wgMemc->get($key);
-		if (is_null ($cached)) {
-			$dbr = &wfGetDB (DB_SLAVE);
-			$query_count = "SELECT COUNT(*) as n FROM ".wfRegexBlockGetTable();
-			$res_count = $dbr->query($query_count);
-			$row_count = $dbr->fetchObject($res_count);
-			$this->numResults = $row_count->n;
-			$wgMemc->set ($key, $this->numResults, REGEXBLOCK_EXPIRE);
-			$dbr->freeResult ($res_count);
+		$key = wfForeignMemcKey( $wgSharedDB, '', REGEXBLOCK_SPECIAL_KEY, REGEXBLOCK_SPECIAL_NUM_RECORD );
+		$cached = $wgMemc->get( $key );
+
+		if ( empty( $cached ) ) {
+			$dbr = wfGetDB( DB_MASTER );
+
+			$oRes = $dbr->select( REGEXBLOCK_TABLE,
+				array("COUNT(*) AS cnt"),
+				array("blckby_blocker <> ''"), 
+				__METHOD__
+			);
+
+			if ( $oRow = $dbr->fetchObject( $oRes ) ) {
+				$this->mNbrResults = $oRow->cnt;
+			}
+			$dbr->freeResult($oRes);
+			$wgMemc->set($key, $this->mNbrResults, REGEXBLOCK_EXPIRE);
 		} else {
-			$this->numResults = $cached;
+			$this->mNbrResults = $cached;
 		}
-	}
 
-	/* on success */
-	function showSuccess () {
-		global $wgOut, $wgRequest;
-		$wgOut->setPageTitle(wfMsg('regexblock-page-title-1'));
-		$wgOut->setSubTitle(wfMsg('regexblock-unblock-success'));
-		$wgOut->addWikiText(wfMsg('regexblock-unblock-log', $wgRequest->getVal('ip', $par)));
+		wfProfileOut( __METHOD__ );
+		return $this->mNbrResults;
 	}
 
-	/* init for showprevnext */
-	function showPrevNext( &$out ) {
-		global $wgContLang, $wgRequest;
-		list( $limit, $offset ) = $wgRequest->getLimitOffset();
-		$filter = 'filter=' . urlencode ( $wgRequest->getVal ('filter') );
-		$html = wfViewPrevNext(
-				$offset,
-				$limit,
-				$wgContLang->specialpage( 'Regexblock' ),
-				$filter,
-				($this->numResults - $offset) <= $limit
-			);
-		$out->addHTML( '<p>' . $html . '</p>' );
+	public function getNbrResults() {
+		return $this->mNbrResults;
 	}
-}
 
-/* the form for blocking names and addresses */
-class regexBlockForm {
-	var $mRegexBlockedAddress, $mRegexBlockedExact, $mRegexBlockedCreation, $mRegexBlockedExpire;
+	/**
+	 * Fetch names of all blockers and write them into select's options
+	 *
+	 * @return $blockers_array
+	 */
+	public function fetchBlockers() {
+		$blockers_array = array();
+		wfProfileIn( __METHOD__ );
 
-	/* constructor */
-	function regexBlockForm ( $par ) {
-		global $wgRequest;
-		$this->mRegexBlockedAddress = $wgRequest->getVal( 'wpRegexBlockedAddress',  $wgRequest->getVal( 'ip', $par ) );
-		$this->mRegexBlockedExact = $wgRequest->getInt('wpRegexBlockedExact');
-		$this->mRegexBlockedCreation = $wgRequest->getInt('wpRegexBlockedCreation');
-		$this->mRegexBlockedExpire = $wgRequest->getVal('wpRegexBlockedExpire');
-		$this->mRegexBlockedReason = $wgRequest->getVal('wpRegexBlockedReason');
+		if ( function_exists( 'wfRegexBlockGetBlockers' ) ) {
+			$blockers_array = wfRegexBlockGetBlockers();
+		} else {
+			global $wgMemc, $wgSharedDB;
+			$key = wfForeignMemcKey( $wgSharedDB, '', REGEXBLOCK_BLOCKERS_KEY );
+			$cached = $wgMemc->get( $key );
+
+			if ( !is_array( $cached ) ) {
+				/* get from database */
+				$dbr = wfGetDB( DB_MASTER );
+				$oRes = $dbr->select( REGEXBLOCK_TABLE,
+					array("blckby_blocker"),
+					array("blckby_blocker <> ''"),
+					__METHOD__,
+					array("GROUP BY" => "blckby_blocker")
+				);
+				while( $oRow = $dbr->fetchObject($oRes) ) {
+					$blockers_array[] = $oRow->blckby_blocker;
+				}
+				$dbr->freeResult($oRes);
+				$wgMemc->set( $key, $blockers_array, REGEXBLOCK_EXPIRE );
+			} else {
+				/* get from cache */
+				$blockers_array = $cached;
+			}
+		}
+
+		wfProfileOut( __METHOD__ );
+		return $blockers_array;
 	}
 
-	/* output */
-	function showForm ( $err ) {
-		global $wgOut, $wgUser, $wgRequest;
+	/**
+	 *
+	 * @param $current
+	 * @param $username
+	 * @param $limit
+	 * @param $offset
+	 * @return $blocker_list
+	 */
+	public function getBlockersData( $current = '', $username = '', $limit, $offset ) {
+		global $wgSharedDB, $wgLang, $wgUser;
 
-		$token = htmlspecialchars( $wgUser->editToken() );
-		$titleObj = Title::makeTitle( NS_SPECIAL, 'Regexblock' );
-		$action = $titleObj->escapeLocalURL( "action=submit" )."&".wfGetListBits();
+		wfProfileIn( __METHOD__ );
 
-		if ( "" != $err ) {
-			$wgOut->setSubtitle( wfMsgHtml( 'formerror' ) );
-			$wgOut->addHTML( "<p class='error'>{$err}</p>\n" );
+		$blocker_list = array();
+		/* get data and play with data */
+		$dbr = wfGetDB( DB_MASTER );
+		$conds = array("blckby_blocker <> ''");
+
+		if ( !empty( $current ) ) {
+			$conds = array("blckby_blocker = {$dbr->addQuotes($current)}");
 		}
 
-		$wgOut->addWikiText (wfMsg('regexblock-help'));
+		if ( !empty( $username ) ) {
+			$conds = array("blckby_name LIKE {$dbr->addQuotes('%'.$username.'%')}");
+		}
 
-		if ( 'submit' == $wgRequest->getVal( 'action' )) {
-			$scRegexBlockedAddress = htmlspecialchars ($this->mRegexBlockedAddress);
-			$scRegexBlockedExpire = htmlspecialchars ($this->mRegexBlockedExpire);
-			$scRegexBlockedReason = htmlspecialchars ($this->mRegexBlockedReason);
-			$this->mRegexBlockedExact ? $checked_ex = "checked=\"checked\"" : $checked_ex = "";
-			$this->mRegexBlockedCreation ? $checked_cr = "checked=\"checked\"" : $checked_cr = "";
-		} else {
-			$scRegexBlockedAddress = '';
-			$checked_ex = '';
-			$checked_cr = '';
+		$oRes = $dbr->select( REGEXBLOCK_TABLE,
+			array("blckby_id, blckby_name, blckby_blocker, blckby_timestamp, blckby_expire, blckby_create, blckby_exact, blckby_reason"),
+			$conds,
+			__METHOD__,
+			array("LIMIT" => $limit, "OFFSET" => $offset, "ORDER BY" => "blckby_id desc")
+		);
+
+		while( $oRow = $dbr->fetchObject($oRes) ) {
+			$ublock_ip = urlencode($oRow->blckby_name);
+			$ublock_blocker = urlencode($oRow->blckby_blocker);
+			$reason = ($oRow->blckby_reason) ? wfMsg('regexblock-form-reason') . $oRow->blckby_reason : wfMsg('regexblock-view-reason-default');
+			$time = $wgLang->timeanddate( wfTimestamp( TS_MW, $oRow->blckby_timestamp ), true );
+
+			/* put data to array */
+			$blocker_list[] = array(
+				'blckby_name' => $oRow->blckby_name,
+				'exact_match' => $oRow->blckby_exact,
+				'create_block' => $oRow->blckby_create,
+				'blocker' => $oRow->blckby_blocker,
+				'reason' => $reason,
+				'time' => $time,
+				'ublock_ip'	=> $ublock_ip,
+				'ublock_blocker' => $ublock_blocker,
+				'expiry' => $oRow->blckby_expire,
+				'blckid' => $oRow->blckby_id
+			);
 		}
-		$blockadressform = wfMsg('regexblock-form-username');
-		$blockadressreason = wfMsg('regexblock-form-reason');
-		$blockadressexpiry = wfMsg('regexblock-form-expiry');
-		$wgOut->addHTML("
-<form name=\"regexblock\" method=\"post\" action=\"{$action}\">
-	<table border=\"0\">
-		<tr>
-			<td align=\"right\">{$blockadressform}</td>
-			<td align=\"left\">
-				<input tabindex=\"1\" name=\"wpRegexBlockedAddress\" size=\"40\" value=\"{$scRegexBlockedAddress}\" />
-			</td>
-		</tr>
-		<tr>
-			<td align=\"right\">{$blockadressreason}</td>
-			<td align=\"left\">
-				<input tabindex=\"2\" name=\"wpRegexBlockedReason\" size=\"40\" value=\"{$scRegexBlockedReason}\" />
-			</td>
-		</tr>
-		<tr>
-			<td align=\"right\">{$blockadressexpiry}</td>
-			<td align=\"left\">
-			<select name=\"wpRegexBlockedExpire\" tabindex=\"3\">");
-			$expiries = array (
-					'1 hour',
-					'2 hours',
-					'4 hours',
-					'6 hours',
-					'1 day',
-					'3 days',
-					'1 week',
-					'2 weeks',
-					'1 month',
-					'3 months',
-					'1 year',
-					'infinite',
-					) ;
-			foreach ($expiries as $duration) {
-				wfRegexBlockMakeOption ($duration, $duration, $scRegexBlockedExpire);
-			}
-		$exactmatch = wfMsg('regexblock-form-match');
-		$blockaccountcreation = wfMsg('regexblock-form-account-block');
-		$submitformblock = wfMsg('regexblock-form-submit');
-		$wgOut->addHTML("</select>
-			</td>
-		</tr>
-		<tr>
-			<td align=\"right\">&#160;</td>
-			<td align=\"left\">
-				<input type=\"checkbox\" tabindex=\"4\" name=\"wpRegexBlockedExact\" id=\"wpRegexBlockedExact\" value=\"1\" $checked_ex />
-				<label for=\"wpRegexBlockedExact\">{$exactmatch}</label>
-			</td>
-		</tr>
-		<tr>
-			<td align=\"right\">&#160;</td>
-			<td align=\"left\">
-				<input type=\"checkbox\" tabindex=\"5\" name=\"wpRegexBlockedCreation\" id=\"wpRegexBlockedCreation\" value=\"1\" $checked_cr />
-				<label for=\"wpRegexBlockedCreation\">{$blockaccountcreation}</label>
-			</td>
-		</tr>
-		<tr>
-			<td align=\"right\">&#160;</td>
-			<td align=\"left\">
-				<input tabindex=\"6\" name=\"wpRegexBlockedSubmit\" type=\"submit\" value={$submitformblock} />
-			</td>
-		</tr>
-	</table>
-	<input type='hidden' name='wpEditToken' value=\"{$token}\" />
-</form>");
+		$dbr->freeResult($oRes);
+
+		wfProfileOut( __METHOD__ );
+		return $blocker_list;
 	}
 
-	/* on success */
-	function showSuccess () {
-		global $wgOut;
-		$wgOut->setPageTitle (wfMsg('regexblock-page-title-1'));
-		$wgOut->setSubTitle (wfMsg('regexblock-block-success'));
+	/**
+	 * Fetch number of all stats rows
+	 *
+	 * @param $id Int: ID of the regexBlock entry (value of stats_blckby_id column in the REGEXBLOCK_STATS_TABLE database table)
+	 * @return $nbrStats
+	 */
+	public function fetchNbrStatResults( $id ) {
+		global $wgSharedDB;
 
-		$wgOut->addWikiText (wfMsg('regexblock-block-log', $this->mRegexBlockedAddress));
-	}
+		wfProfileIn( __METHOD__ );
+		$nbrStats = 0;
 
-	/* on submit */
-	function doSubmit () {
-		global $wgOut, $wgUser, $wgMemc;
+		$dbr = wfGetDB( DB_SLAVE );
+		$oRes = $dbr->select( REGEXBLOCK_STATS_TABLE,
+			array("COUNT(*) AS cnt"),
+			array("stats_blckby_id = '".intval($id)."'"),
+			__METHOD__
+		);
 
-		/* empty name */
-		if ( strlen($this->mRegexBlockedAddress) == 0 ) {
-			$this->showForm (wfMsg('regexblock-form-submit-empty'));
-			return;
+		if ( $oRow = $dbr->fetchObject($oRes) ) {
+			$nbrStats = $oRow->cnt;
 		}
+		$dbr->freeResult($oRes);
 
-		/* castrate regexes */
-		if (!$simple_regex = wfValidRegex ($this->mRegexBlockedAddress) ) {
-			/* now, very generic comment - should the conditions change, this should too */
-			$this->showForm (wfMsg('regexblock-form-submit-regex'));
-			return;
-		}
+		wfProfileOut( __METHOD__ );
+		return $nbrStats;
+	}
 
-		/* check expiry */
-		if ( strlen ($this->mRegexBlockedExpire) == 0 ) {
-			$this->showForm (wfMsg('regexblock-form-submit-expiry'));
-			return;
+	/**
+	 * Fetch all logs
+	 *
+	 * @param $id Int: ID of the regexBlock entry (value of stats_blckby_id column in the REGEXBLOCK_STATS_TABLE database table)
+	 * @param $limit
+	 * @param $offset
+	 * @return $stats
+	 */
+	public function getStatsData( $id, $limit = 50, $offset = 0 ) {
+		global $wgSharedDB;
+
+		wfProfileIn( __METHOD__ );
+		$stats = array();
+
+		/* from database */
+		$dbr = wfGetDB( DB_SLAVE );
+		$conds = array("stats_blckby_id = '".intval($id)."'");
+		$oRes = $dbr->select( REGEXBLOCK_STATS_TABLE,
+			array("stats_blckby_id", "stats_user", "stats_blocker", "stats_timestamp", "stats_ip", "stats_match", "stats_dbname"),
+			$conds,
+			__METHOD__,
+			array("LIMIT" => $limit, "OFFSET" => $offset, "ORDER BY" => "stats_timestamp DESC")
+		);
+
+		while( $oRow = $dbr->fetchObject($oRes) ) {
+			$stats[] = $oRow;
 		}
+		$dbr->freeResult($oRes);
 
-		/* TODO - check infinite */
-		if ($this->mRegexBlockedExpire != 'infinite') {
-			$expiry = strtotime( $this->mRegexBlockedExpire );
-		if ( $expiry < 0 || $expiry === false ) {
-			$this->showForm( wfMsg( 'ipb_expiry_invalid' ) );
-			return;
+		wfProfileOut( __METHOD__ );
+		return $stats;
+	}
+
+	/**
+	 * Fetch record for selected identifier of regex block
+	 *
+	 * @param $id Int: ID of the regexBlock entry (value of blckby_id column in the REGEXBLOCK_STATS_TABLE database table)
+	 * @return $record
+	 */
+	public function getRegexBlockById( $id ) {
+		global $wgSharedDB;
+
+		wfProfileIn( __METHOD__ );
+		$record = null;
+
+		$dbr = wfGetDB( DB_MASTER );
+		$oRes = $dbr->select( REGEXBLOCK_TABLE,
+			array("blckby_id", "blckby_name", "blckby_blocker", "blckby_timestamp", "blckby_expire", "blckby_create", "blckby_exact", "blckby_reason"),
+			array("blckby_id = '".intval($id)."'"),
+			__METHOD__
+		);
+
+		if( $oRow = $dbr->fetchObject($oRes) ) {
+			$record = $oRow;
 		}
-		$expiry = wfTimestamp( TS_MW, $expiry );
-		} else {
-			$expiry = $this->mRegexBlockedExpire;
-		}
+		$dbr->freeResult($oRes);
+		
+		wfProfileOut( __METHOD__ );
+		return $record;
+	}
 
+	/**
+	 * Insert a block record to the REGEXBLOCK_TABLE database table
+	 *
+	 * @param $address
+	 * @param $expiry Mixed: expiry time of the block
+	 * @param $exact
+	 * @param $creation
+	 * @param $reason Mixed: given block reason, which will be displayed to the regexblocked user
+	 */
+	static public function blockUser( $address, $expiry, $exact, $creation, $reason ) {
+		global $wgUser;
+
+		wfProfileIn( __METHOD__ );
 		/* make insert */
-		$dbw =& wfGetDB( DB_MASTER );
+		$dbw = wfGetDB( DB_MASTER );
 		$name = $wgUser->getName();
-		$timestamp =  wfTimestampNow();
 
-		$query = "INSERT IGNORE INTO ".wfRegexBlockGetTable()."
-			  (blckby_id, blckby_name, blckby_blocker, blckby_timestamp, blckby_expire, blckby_exact, blckby_create, blckby_reason)
-			  VALUES (null,
-				  {$dbw->addQuotes($this->mRegexBlockedAddress)},
-				  {$dbw->addQuotes($name)},
-				  '{$timestamp}',
-				  '{$expiry}',
-				  {$this->mRegexBlockedExact},
-				  {$this->mRegexBlockedCreation},
-				  {$dbw->addQuotes($this->mRegexBlockedReason)}
-				 )";
-		$dbw->query($query);
-		/* duplicate entry */
-		if (!$dbw->affectedRows()) {
-			$this->showForm (wfMsg('regexblock-already-blocked', $this->mRegexBlockedAddress));
-			return;
+		$oRes = $dbw->replace( REGEXBLOCK_TABLE,
+			array( 'blckby_id', 'blckby_name' ),
+			array(
+				'blckby_id' => 'null',
+				'blckby_name' => $address, 
+				'blckby_blocker' => $name, 
+				'blckby_timestamp' => wfTimestampNow(),
+				'blckby_expire' => $expiry,
+				'blckby_exact' => intval($exact),
+				'blckby_create' => intval($creation),
+				'blckby_reason' => $reason
+			),
+			__METHOD__
+		);
+
+		wfProfileOut( __METHOD__ );
+		return true;
+	}
+
+	/**
+	 * Gets and returns the expiry time values
+	 *
+	 * @return array Array of block expiry times
+	 */
+	static public function getExpireValues() {
+		$expiry_values = explode( ",", wfMsg('regexblock-expire-duration') );
+		$expiry_text = array('1 hour', '2 hours', '4 hours', '6 hours', '1 day', '3 days', '1 week', '2 weeks', '1 month', '3 months', '6 months', '1 year', 'infinite');
+
+		if ( !function_exists('array_combine') ) {
+			function array_combine($a, $b) {
+				$out = array();
+				foreach( $a as $k => $v ) {
+					$out[$v] = $b[$k];
+				}
+				return $out;
+			}
 		}
 
-		wfRegexBlockUnsetKeys ($name, $this->mRegexBlockedAddress);
+		return array_combine($expiry_text, $expiry_values);
+	}
 
-		/* redirect */
-		$titleObj = Title::makeTitle( NS_SPECIAL, 'Regexblock' ) ;
-		$wgOut->redirect( $titleObj->getFullURL( 'action=success_block&ip=' .urlencode( $this->mRegexBlockedAddress )."&".wfGetListBits() ) );
-	}
+	/**
+	 * Check that the given regex is valid
+	 *
+	 * @param $text Mixed: regular expression to be tested for validity
+	 */
+	static function isValidRegex( $text ) {
+		return ( sprintf( "%s", @preg_match("/{$text}/", 'regex') ) === '' );
+	}	
 }
\ No newline at end of file
Index: trunk/extensions/regexBlock/README
===================================================================
--- trunk/extensions/regexBlock/README	(revision 45427)
+++ trunk/extensions/regexBlock/README	(revision 45428)
@@ -1,49 +1,16 @@
+This is the readme file for regexBlock extension.
 
-# INSTALLATION 
+==Authors==
+regexBlock was written by Bartek Łapiński, Tomasz Klim, Piotr Molski and Adrian Wieczorek for Wikia, Inc.
 
-# Note: this extension works best when used along with setting shared database and memcached 
+==License==
+regexBlock is licensed under GNU General Public License 2.0 or later.
+See http://www.gnu.org/copyleft/gpl.html for more details.
 
-# 1. Copy the /regexBlock folder and regexBlock.php into the /extensions folder.
-# 2. Add require_once ("/extensions/regexBlock.php") ;
-#    to your GlobalSettings.php file. 
-# 3. Create required tables.
-# 4. Set $wgSharedDB to the name of a shared database of your choice (in GlobalSettings.php).
-# 5. Set $wgMainCacheType to 'CACHE_MEMCACHED' and $wgMemCachedServers. The latter could look like this:
-#    $wgMemCachedServers = array ("127.0.0.1:11000"); (for a memcached server running on IP 127.0.0.1 and
-#    port 11000).
+==Installing regexBlock==
+Installation instructions and more details can be found on the extension's infopage:
+http://www.mediawiki.org/wiki/Extension:RegexBlock
 
-# Depending on your previous configuration, you may need to remove or comment a line including the older regexBlock.php
-# (probably in your GlobalSettings.php file).
-
-
-# Required tables
-# total: 2 tables
-
-	CREATE TABLE `blockedby` (
-		`blckby_id` int(5) NOT NULL auto_increment,
-		`blckby_name` varchar(255) NOT NULL,
-		`blckby_blocker` varchar(255) NOT NULL,
-		`blckby_timestamp` char(14) NOT NULL,
-		`blckby_expire` char(14) NOT NULL,
-		`blckby_create` tinyint(1) NOT NULL default '1',
-		`blckby_exact` tinyint(1) NOT NULL default '0',
-		`blckby_reason` tinyblob NOT NULL,
-		PRIMARY KEY  (`blckby_id`),
-		UNIQUE KEY `blckby_name` (`blckby_name`),
-		KEY `blckby_timestamp` (`blckby_timestamp`),
-		KEY `blckby_expire` (`blckby_expire`)
-		) ;
-
-	CREATE TABLE `stats_blockedby` (
-			`stats_id` int(8) NOT NULL auto_increment,
-			`stats_user` varchar(255) NOT NULL,
-			`stats_blocker` varchar(255) NOT NULL,
-			`stats_timestamp` char(14) NOT NULL,
-			`stats_ip` char(15) NOT NULL,
-			PRIMARY KEY  (`stats_id`),
-			KEY `stats_timestamp` (`stats_timestamp`)
-			) ;
-
-
-# More details can be found in the included RegexBlock.xml (mediawiki XML
-# dump format) or RegexBlock.html files
+==Bugs==
+Bugs and issues can be reported on the extension's talk page:
+http://www.mediawiki.org/w/index.php?title=Extension_talk:RegexBlock&action=edit&section=new
\ No newline at end of file
Index: trunk/extensions/regexBlock/schema.sql
===================================================================
--- trunk/extensions/regexBlock/schema.sql	(revision 0)
+++ trunk/extensions/regexBlock/schema.sql	(revision 45428)
@@ -0,0 +1,28 @@
+CREATE TABLE `blockedby` (
+	`blckby_id` int(5) NOT NULL AUTO_INCREMENT,
+	`blckby_name` varchar(255) NOT NULL,
+	`blckby_blocker` varchar(255) NOT NULL,
+	`blckby_timestamp` char(14) NOT NULL,
+	`blckby_expire` char(14) NOT NULL,
+	`blckby_create` tinyint(1) NOT NULL DEFAULT '1',
+	`blckby_exact` tinyint(1) NOT NULL DEFAULT '0',
+	`blckby_reason` tinyblob NOT NULL,
+	PRIMARY KEY  (`blckby_id`),
+	UNIQUE KEY `blckby_name` (`blckby_name`),
+	KEY `blckby_timestamp` (`blckby_timestamp`),
+	KEY `blckby_expire` (`blckby_expire`)
+) ENGINE=InnoDB;
+
+CREATE TABLE `stats_blockedby` (
+	`stats_id` int(8) NOT NULL AUTO_INCREMENT,
+	`stats_blckby_id` int(8) NOT NULL,
+	`stats_user` varchar(255) NOT NULL,
+	`stats_blocker` varchar(255) NOT NULL,
+	`stats_timestamp` char(14) NOT NULL,
+	`stats_ip` char(15) NOT NULL,
+	`stats_match` varchar(255) NOT NULL default '',
+	`stats_dbname` varchar(255) NOT NULL default '',
+	PRIMARY KEY  (`stats_id`),
+	KEY `stats_blckby_id_key` (`stats_blckby_id`),
+	KEY `stats_timestamp` (`stats_timestamp`)
+) ENGINE=InnoDB;
\ No newline at end of file

Property changes on: trunk/extensions/regexBlock/schema.sql
___________________________________________________________________
Name: svn:eol-style
   + native

Status & tagging log

Views
Toolbox