r45428 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r45427‎ | r45428 | 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:
  • /trunk/extensions/regexBlock/README (modified) (history)
  • /trunk/extensions/regexBlock/RegexBlock.html (deleted) (history)
  • /trunk/extensions/regexBlock/RegexBlock.xml (deleted) (history)
  • /trunk/extensions/regexBlock/SpecialRegexBlock.php (modified) (history)
  • /trunk/extensions/regexBlock/SpecialRegexBlockStats.php (deleted) (history)
  • /trunk/extensions/regexBlock/patch-upgrade.sql (added) (history)
  • /trunk/extensions/regexBlock/regexBlock.i18n.php (modified) (history)
  • /trunk/extensions/regexBlock/regexBlock.php (modified) (history)
  • /trunk/extensions/regexBlock/regexBlockCore.php (modified) (history)
  • /trunk/extensions/regexBlock/schema.sql (added) (history)

Diff [purge]

Index: trunk/extensions/regexBlock/RegexBlock.xml
@@ -1,357 +0,0 @@
2 -<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/">
3 - <siteinfo>
4 - <sitename>Wikia</sitename>
5 - <base>http://www.wikia.com/wiki/Main_Page
6 - <generator>MediaWiki 1.7alpha</generator>
7 - <case>first-letter</case>
8 - <namespaces>
9 - <namespace key="-2">Media</namespace>
10 - <namespace key="-1">Special</namespace>
11 - <namespace key="0"></namespace>
12 - <namespace key="1">Talk</namespace>
13 - <namespace key="2">User</namespace>
14 - <namespace key="3">User talk</namespace>
15 - <namespace key="4">Staff Wiki</namespace>
16 - <namespace key="5">Staff Wiki talk</namespace>
17 - <namespace key="6">Image</namespace>
18 - <namespace key="7">Image talk</namespace>
19 - <namespace key="8">MediaWiki</namespace>
20 - <namespace key="9">MediaWiki talk</namespace>
21 - <namespace key="10">Template</namespace>
22 - <namespace key="11">Template talk</namespace>
23 - <namespace key="12">Help</namespace>
24 - <namespace key="13">Help talk</namespace>
25 - <namespace key="14">Category</namespace>
26 - <namespace key="15">Category talk</namespace>
27 - <namespace key="100">Portal</namespace>
28 - <namespace key="101">Portal talk</namespace>
29 - <namespace key="102">Blog</namespace>
30 - </namespaces>
31 - </siteinfo>
32 - <page>
33 - <title>RegexBlock</title>
34 - <id>1873</id>
35 - <revision>
36 - <id>6278</id>
37 - <timestamp>2006-12-29T20:42:46Z</timestamp>
38 - <contributor>
39 - <username>Sannse</username>
40 - <id>5</id>
41 - </contributor>
42 - <comment>/* Help page */</comment>
43 - <text space="preserve">:&lt;small&gt;See
44 -[[Cross wiki user blocks]] for a list of users blocked the old
45 -way.&lt;/small&gt;
46 -&lt;H1&gt;Product Requirements&lt;/H1&gt;
47 -==Product Description==
48 -: This is the automated version of the original regexBlock extension.
49 -It takes data out of shared the database instead out of hardwritten
50 -text inside of the file (''uses memcached to speed up the operations,
51 -mostly on the side of checking edits'') and applies blocking to all
52 -users, groupped by blocker's names. There is also a Special Page
53 -(restricted to users with the preexisting 'block' ability) that allows
54 -to block, view (also view by blockers) and unblock user names and IP
55 -addresses. Current version also allows to block entire IP addresses (no
56 -range or partial blockings in this case).
57 -: Will also include in this one an ability to prevent new account
58 -creation (yes, such a thing already exists in IP Blocks, but is not
59 -multi-wikia-wide).
60 -==Product Assumptions==
61 -* we use a shared database (so the block is enabled on multiple wikias)
62 -* we use memcached (so database operations are sped up)
63 -* user of this special page should have the rights to block
64 -* we take care of account creation prevention multi-wikia style right
65 -here
66 -==Product Features==
67 -:;Basic functionality
68 -:: Ability to block users by their names when they try to edit pages,
69 -getting data out of the database, special page for managing blocks.
70 -:;Extended functionality
71 -:: Ability to block IP addresses, use of memcached, viewing blocks by
72 -blockers.
73 -:;Additional functionality
74 -:: Ability to block new account creation on multiple wikias, displaying
75 -efficiency statistics for particular blocks (who, when, from where
76 -tried to edit and was blocked), allowing to set expiration period for
77 -blocks (which is already done).
78 -==Key Product Concepts==
79 -:;Data fetched from shared database
80 -:: Data should be fetched from a shared database, supported with the
81 -use of memcached instead of hardwritten from the file.
82 -:;Submit form on one page with the list
83 -:: Easier to manage than having them on two separated special pages.
84 -:;May be extended by having more statistics, timestamps, allow blocks
85 -by IP ranges, and such
86 -:: We can add some statistics into separate tables (like the reason -
87 -rather an internal note for admin, why this particular user has been
88 -blocked, or the IP's from which the user tried to login etc.).
89 -==Product Unknowns==
90 -* Should we have a similar page for usernames excluded from blocking
91 -(when we block some IPs)? This could be linked to this project (the new
92 -special page would fetch some data from this one).
93 -* Include ability to block entire IP ranges? There are already
94 -indications that it would be useful.
95 -* Include timestamps? For now, the blocks are there for infinity.
96 -** Timestamps are now included. Expired blocks are deleted
97 -automatically when a blocked user tries to edit and an expired block is
98 -then applied. In such case, the block is discarded and user can edit.
99 -Admin can also remove expired blocks manually from list (they are
100 -denoted 'EXPIRED' in red font)
101 -* Show more information? Like for example statistics, which users tried
102 -to login from which IPs and how many times, etc.
103 -==Potential Product Issues==
104 -* As we are able to block entire IP addresses, so there is more need
105 -for a utility to allow specific users identified by their usernames to
106 -edit anyway, even when they log from the blocked address. Similar
107 -request already exists here:
108 -[[Product_Priorities:_Ops_Upgrades_Bugs#General]]. The special page to
109 -exclude blocks could integrate both IP Blocks and Regexblocks or it
110 -could be just two separate special pages.
111 -* &lt;strike&gt;The blocks are permanent at the moment. So there is
112 -more attention required to them.&lt;/strike&gt;
113 -: Which is untrue now. The blocks can be made with a chosen expiration
114 -time. After that, then can be removed manually or on block. Either way,
115 -they will be harmless.
116 -* also a note: on older versions of MW when the user is regexblocked
117 -(s)he cannot also create accounts (same goes for normal IP blocks), on
118 -newer (I've tested it personally on 1.8.2) it allows a separate user
119 -account creation block - it's about the
120 -code, and since I was developing a clean extension, I didn't want to
121 -alter the MW code itself. This leads to a situation, when the same
122 -block of one vandal will allow him/her to create account on one wiki,
123 -and disallow on the other (assuming that they will have different
124 -versions and the option 'block account creation' will be unchecked).
125 -==Product Interface==
126 -: Has one Special Page named Regexblock. It displays a form, used for
127 -adding blocks, and a list, used for managing blocks.
128 -: The form has a field, specyfing user name or IP that we want to
129 -block. While not required for a successful block, there are three more
130 -options: whether match should be '''exact''', whether we should deny
131 -this user '''account creation''', or the '''expiration period''', after
132 -which the block will be invalid.
133 -: The list can be filtered by usernames of the blockers. Each row
134 -displays following information: ''username or IP address blocked'',
135 -''username of the blocker'' and a ''link to unblock''. The unblock
136 -requires no confirmation and is processed instantenuosly. The list can
137 -be traversed as all standard lists in special pages (prev, next,
138 -display by 20, 50 etc.). Each row also has the link to '''statistics'''
139 -for a particular block.
140 -==Product Structure==
141 -* '''Files'''
142 -** '''regexBlock.php''' ''contains includes for the files in the
143 -folder, this one replaces old regexBlock.php
144 -** '''/regexBlock''' ''folder containg the core of the extension plus
145 -docs''
146 -*** '''README''' ''contains installation instructions and product
147 -description''
148 -*** '''regexBlockCore.php''' ''contains the blocking utility and use of
149 -the hook''
150 -*** '''SpecialRegexBlock.php''' ''Special Page for viewing and managing
151 -block list''
152 -*** '''SpecialRegexBlockStats.php''' ''Special Page for displaying
153 -stats for particular blocks''
154 -*** '' '''SimplifiedRegex.php''' - this one is required also by
155 -[[SpamRegex]] ''
156 -* '''SQL''' CREATE TABLE `blockedby` ( `blckby_id` int(5) NOT NULL
157 -auto_increment, `blckby_name` varchar(255) character set latin1 collate
158 -latin1_bin NOT NULL, `blckby_blocker` varchar(255) character set latin1
159 -collate latin1_bin NOT NULL, `blckby_timestamp` char(14) NOT NULL,
160 -`blckby_expire` char(14) NOT NULL, `blckby_create` tinyint(1) NOT NULL
161 -default '1', `blckby_exact` tinyint(1) NOT NULL default '0',
162 -`blckby_reason` tinyblob NOT NULL, PRIMARY KEY (`blckby_id`), UNIQUE
163 -KEY `blckby_name` (`blckby_name`), KEY `blckby_timestamp`
164 -(`blckby_timestamp`), KEY `blckby_expire` (`blckby_expire`) ) ; CREATE
165 -TABLE `stats_blockedby` ( `stats_id` int(8) NOT NULL auto_increment,
166 -`stats_user` varchar(255) character set latin1 collate latin1_bin NOT
167 -NULL, `stats_blocker` varchar(255) character set latin1 collate
168 -latin1_bin NOT NULL, `stats_timestamp` char(14) NOT NULL, `stats_ip`
169 -char(15) NOT NULL, `stats_match` varchar(255) character set latin1
170 -collate latin1_bin NOT NULL default '-', `stats_wiki` varchar(255) NOT
171 -NULL default '', PRIMARY KEY (`stats_id`), KEY `stats_timestamp`
172 -(`stats_timestamp`) ) ;
173 -* '''Installation'''
174 -# Create tables in the shared database.
175 -# Copy folder /regexBlock to /extensions, copy SimplifiedRegex.php to
176 -/extensions ('''do not''' copy regexBlock.php yet!).
177 -# Edit regexBlock.php and set '''REGEXBLOCK_PATH''' to its correct
178 -value.
179 -# Copy regexBlock.php to /extensions.
180 -:That should do the trick. If people belonging to staff cannot access
181 -the extension, check if relogin helps. If not, please check if the
182 -staff permissions are not '''overwritten''' after the initialization of
183 -the extension. If that is the case, the permissions should be rather
184 -appended (the extension adds one new permission right).
185 -==Product Use Cases==
186 -# '''Blocking a vandalising user by a username'''
187 -::A user is vandalising over different wikias. An admin blocks the user
188 -by adding his/her username to the block list. The user cannot edit
189 -pages anymore until the block is released.
190 -::;Step 1 (An admin blocks user)'''
191 -:::Admin inserts the username into the database via the special page
192 -regexblock.
193 -::;Step 2 (The user cannot edit pages)
194 -:::Blocked user (a user that his/her username '''contains''' the
195 -blocked username) tries to edit. There goes a block check, and there is
196 -a match. Now, the expiration check is done. If the block is still
197 -valid, unfortunate (vandalising) user will be displayed a message that
198 -this particular user is blocked, along with the name of the blocker.
199 -Also, on a successful block, an entry into the database is made.
200 -::;Step 3 (Admin releases the block)
201 -:::When the situation is under control, admin can ublock the user (or
202 -not, if the user's actions made clear that the block should be
203 -permanent). Admin can also view statistics for this particular block
204 -(frequent attempts to edit despite being blocked may encourage admin to
205 -make a more permanent block...).
206 -# '''Blocking a vandalising user by an IP address'''
207 -:: A user is vandalising over different wikias. An admin blocks the
208 -user by adding his/her IP address to the block list. No users logging
209 -from that IP will be able to edit the pages.
210 -::;Step 1 (An admin blocks '''full''' IP address)
211 -:::Admin inserts the '''full''' IP address into the database via the
212 -special page regexblock (''a partial or range IP address will be
213 -treated like a username. This is to avoid situations such as when a '3'
214 -block blocks all IP's including '3'. Regular expressions.''). ::;Step 2
215 -(The user cannot edit pages)
216 -:::When a user logging in from a particular IP address (it has to be an
217 -'''exact''' match) tries to edit, he/she will be displayed a message
218 -that this particular user is blocked, along with the name of the
219 -blocker. Also, all non-logged users from that IP address will be
220 -blocked.
221 -::;Step 3 (Admin releases the block)
222 -:::When the situation is under control, admin can ublock this IP
223 -address (or not, if the user's actions made clear that the block should
224 -be permanent).
225 -==Product Bugs==
226 -:Please test it on http://fp006.qa.wikia.com/wiki/Special:Regexblock.
227 -(it requires staff group rights). Account creation prevention mode may
228 -not work properly at the moment - still working on that.
229 -:'''Known Bugs''':
230 -:* there is a little glitch when someone unblocks the last block by a
231 -particular blocker (it shows 'All' on the select, but the list is
232 -empty, the only exception is when unblocked row was the last on the
233 -global list)
234 -==Discussion==
235 -[[User:Kaz3t]]: I'm not convinced that currently way for blocking user
236 -by name is the best. For example user name can consist of his real name
237 -and surname. So if we block user "John" we also block users "Johnny",
238 -"John Smith", etc. In my opinion we should have choice - multi-block
239 -(as it is now) and single-block (for example when we want to block this
240 -and only this specified user we put his name between quotation-marks).
241 -::: Yes, you got a point. Umm, but wouldn't a checkbox be better? It
242 -would be more obvious on the first sight, I believe.
243 -::: I added a checkbox just for that, should be working fine.
244 -* The message that a user sees is the same whether it is a similar name
245 -block or a specific name block (or even an IP block). I think this
246 -needs to either be made more general, or made variable. The reason set
247 -is: "This username is prevented from editing due to vandalism by a user
248 -with a similar name. Please create an alternate user name or contact
249 -Wikia about the problem"
250 -:::: There are four types of messages now (displayed to the blocked
251 -user). First, is the reason specified by an admin. If there is none,
252 -different messages will be displayed for: exact username match, IP
253 -address match and a regex username match. - --[[User:Bartek|Bartek]]
254 -13:07, 24 November 2006 (UTC)
255 -* Is it possible to block ''just'' creation of accounts? for example to
256 -stop future creation of sannse-like accounts while still allowing me to
257 -edit? (this isn't vital though)
258 -:::: Good point. For the moment, it is not possible with this
259 -extension. It will be added in the future. - --[[User:Bartek|Bartek]]
260 -09:42, 24 November 2006 (UTC)
261 -* When it's an exact match block, this is mentioned on the listing.
262 -Could it also be
263 -mentioned when it is not exact? It would make this listing clearer. For
264 -example:
265 -** Bartek (exact match) (account creation block) (blocked by: Sannse)
266 -on (etc.)
267 -** Bartek (regex match) (account creation block) (blocked by: Sannse)
268 -on (etc.)
269 -:::: Changed already --[[User:Bartek|Bartek]] 09:34, 24 November 2006
270 -(UTC)
271 -*
272 -http://fp006.qa.wikia.com/index.php?title=Special:Regexblockstats&amp;target=sannse
273 -- I think all other logs have newes entries at the top. Can this one be
274 -reversed?
275 -:::: Done - --[[User:Bartek|Bartek]] 09:39, 24 November 2006 (UTC)
276 -* I think that's all I can come up with, this is wonderful! thanks so
277 -much for getting this going :) -- [[User:Sannse|Sannse]] 18:13, 23
278 -November 2006 (UTC)
279 -* The list of blocks is in alphabetical order. Is this the best way? I
280 -think it may be, I'm just used to seeing such things in time order.
281 -:::: Right. For the time being, it is in a descending time order. Feel
282 -free to test and I will change to what will be best. -
283 -* Expired blocks are shown - useful? or are they quickly going to fill
284 -up the blocklog?
285 -:::: Now they are hidden. - --[[User:Bartek|Bartek]] 14:03, 27 November
286 -2006 (UTC)
287 -* All seems to work as advertised :) -- [[User:Sannse|Sannse]] 11:32,
288 -27 November 2006 (UTC)
289 -* A community team question. Do we want to change the default block
290 -reasons at all? The current defaults are:
291 -** "This IP address is prevented from editing due to vandalism" (IP
292 -block)
293 -** "This username is prevented from editing due to vandalism by a user
294 -with a similar name. Please create an alternate user name or contact
295 -Wikia about the problem" (RegEx block)
296 -** "This username is prevented from editing due to vandalism" (Username
297 -block)
298 -:It won't always be vandalism. Is there something more general we can
299 -use to cover vandalism, spamming, trolling, impersonation, etc?
300 -[[User:Angela|Angela]] 10:53, 28 November 2006 (UTC)
301 -:: how about "vandalism or other disruption" -- [[User:Sannse|Sannse]]
302 -08:59, 30 November 2006 (UTC)
303 -* as Splarka said earlier (05.12.2006) on IRC - ''an idea for blocking
304 -IP ranges: a radio box: (*) whole username (exact match) ( ) regex
305 -username ( ) IP or IP range in the first: "Splarka" would block just
306 -that name... in the second "Spla" would block any username containing
307 -that... and in the third: 75.72.55.78 would block just the user's IP,
308 -and 75.72. could block 75.72.*.* simple summary: IP matching would be
309 -made manual (easier to code probably?) although CIDR support might be
310 -better than regex for IPs... eg 75.72.0.0/16''
311 -* as Sannse and Splarka said today on IRC, it would be good to have an
312 -option to block users in case-sensitive mode. One proposition is for an
313 -exact match to be case-sensitive. - --[[User:Bartek|Bartek]] 09:06, 18
314 -December 2006 (UTC)
315 -:: As agreed, it will be case-sensitive for an exact match mode. -
316 -:::Which is already under testing
317 -* and, as Emil said, it would be good to check if there is some proxy
318 -check within the IP check (might be already implemented in existing
319 -functions, since I am using class User's function to get the user's IP)
320 -==Suggested messages==
321 -* "This IP address is prevented from editing due to vandalism or other
322 -disruption by you or by someone who shares your IP address. If you
323 -believe this is in error, please [[Special:Contact|contact Wikia]]."
324 -(IP block)
325 -* "This username is prevented from editing due to vandalism or other
326 -disruption by a user with a similar name. Please create an alternate
327 -user name or [[Special:Contact|contact Wikia]] about the problem."
328 -(RegEx block)
329 -* "This username is prevented from editing due to vandalism or other
330 -disruption. If you believe this is in error, please
331 -[[Special:Contact|contact Wikia]]." (Username block)
332 -==Help page==
333 -[[Help:Global blocks]] (for contributors, not feature users)
334 -==Post release==
335 -A possible problem that's hard to confirm.
336 -User:WatchTVEatDonutDrinkBeer complained of errors on trying to access
337 -pages on wikiality. Their browser would direct them to
338 -http://84.40.25.253/PageName
339 -and they would get the 404 message: Not Found The requested URL
340 -/Main_Page was not found on this server. Apache/2.2.0 (Unix)
341 -mod_ssl/2.2.0 OpenSSL/0.9.7f PHP/5.1.4 Server at 84.40.25.253 Port 80
342 -Because the timing of their last edit was so similar to a block Angela
343 -had made: # wheels! (regex match) (account creation block) (blocked by:
344 -Angela, generic reason) on 19:43, 29 December 2006 (unblock) permanent
345 -block (stats)
346 -I tried removing this block. Once I'd done that, he could access the
347 -site normally. Replacing the block had no effect. I compared his IP
348 -with the vandal's (User:Willy on wheels!) but they didn't match.
349 -Perhaps a coincidence? But recording it here just in case it isn't --
350 -[[User:Sannse|Sannse]] 20:42, 29 December 2006 (UTC)
351 -[[Category:Features]]</text>
352 - </revision>
353 - </page>
354 -</mediawiki>
Index: trunk/extensions/regexBlock/RegexBlock.html
@@ -1,416 +0,0 @@
2 -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3 -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" dir="ltr" lang="en"><head>
4 - <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5 - <link rel="copyright" href="http://www.gnu.org/copyleft/fdl.html"><title>RegexBlock</title>
6 -
7 - <style type="text/css" media="screen,projection">/*<![CDATA[*/ @import "/skins3/monobook/main.css?0"; /*]]>*/</style>
8 - <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>
9 - <meta http-equiv="imagetoolbar" content="no" /><![endif]-->
10 -
11 - <script type="text/javascript">var skin = 'monobook';var stylepath = '/skins3';</script>
12 - <script type="text/javascript" src="_files/wikibits"><!-- wikibits js --></script>
13 - <script type="text/javascript" src="_files/index"><!-- site js --></script>
14 - <style type="text/css">/*<![CDATA[*/
15 -@import "/index.php?title=MediaWiki:Common.css&action=raw&ctype=text/css&smaxage=18000";
16 -@import "/index.php?title=MediaWiki:Monobook.css&action=raw&ctype=text/css&smaxage=18000";
17 -@import "/index.php?title=-&action=raw&gen=css&maxage=18000&ts=20070117004616";
18 -/*]]>*/</style>
19 - <script type="text/javascript" src="_files/index_002"></script><!-- Head Scripts --></head>
20 -
21 -<body class="ns-0">
22 -
23 -<div id="globalWrapper">
24 - <div id="column-content">
25 - <div id="content" class="content-ads">
26 -
27 -
28 - <a name="top" id="top"></a>
29 - <h1 class="firstHeading">RegexBlock</h1>
30 -
31 -
32 - <div id="bodyContent">
33 - <h3 id="siteSub">Wikia</h3>
34 - <div id="contentSub"></div>
35 - <!-- start content -->
36 -
37 -<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>
38 -<ul>
39 -<li class="toclevel-1"><a href="#Product_Requirements"><span class="tocnumber">1</span> <span class="toctext">Product Requirements</span></a>
40 -<ul>
41 -<li class="toclevel-2"><a href="#Product_Description"><span class="tocnumber">1.1</span> <span class="toctext">Product Description</span></a></li>
42 -<li class="toclevel-2"><a href="#Product_Assumptions"><span class="tocnumber">1.2</span> <span class="toctext">Product Assumptions</span></a></li>
43 -<li class="toclevel-2"><a href="#Product_Features"><span class="tocnumber">1.3</span> <span class="toctext">Product Features</span></a></li>
44 -<li class="toclevel-2"><a href="#Key_Product_Concepts"><span class="tocnumber">1.4</span> <span class="toctext">Key Product Concepts</span></a></li>
45 -<li class="toclevel-2"><a href="#Product_Unknowns"><span class="tocnumber">1.5</span> <span class="toctext">Product Unknowns</span></a></li>
46 -<li class="toclevel-2"><a href="#Potential_Product_Issues"><span class="tocnumber">1.6</span> <span class="toctext">Potential Product Issues</span></a></li>
47 -<li class="toclevel-2"><a href="#Product_Interface"><span class="tocnumber">1.7</span> <span class="toctext">Product Interface</span></a></li>
48 -<li class="toclevel-2"><a href="#Product_Structure"><span class="tocnumber">1.8</span> <span class="toctext">Product Structure</span></a></li>
49 -<li class="toclevel-2"><a href="#Product_Use_Cases"><span class="tocnumber">1.9</span> <span class="toctext">Product Use Cases</span></a></li>
50 -<li class="toclevel-2"><a href="#Product_Bugs"><span class="tocnumber">1.10</span> <span class="toctext">Product Bugs</span></a></li>
51 -<li class="toclevel-2"><a href="#Discussion"><span class="tocnumber">1.11</span> <span class="toctext">Discussion</span></a></li>
52 -<li class="toclevel-2"><a href="#Suggested_messages"><span class="tocnumber">1.12</span> <span class="toctext">Suggested messages</span></a></li>
53 -<li class="toclevel-2"><a href="#Help_page"><span class="tocnumber">1.13</span> <span class="toctext">Help page</span></a></li>
54 -<li class="toclevel-2"><a href="#Post_release"><span class="tocnumber">1.14</span> <span class="toctext">Post release</span></a></li>
55 -</ul>
56 -</li>
57 -</ul>
58 -</td></tr></tbody></table><script type="text/javascript"> if (window.showTocToggle) { var tocShowText = "show"; var tocHideText = "hide"; showTocToggle(); } </script>
59 -<a name="Product_Requirements"></a><h1>Product Requirements</h1>
60 -<a name="Product_Description"></a><h2>Product Description</h2>
61 -<dl><dd> This is the automated version of the original regexBlock
62 -extension. It takes data out of shared the database instead out of
63 -hardwritten text inside of the file (<i>uses memcached to speed up the operations, mostly on the side of checking edits</i>)
64 -and applies blocking to all users, groupped by blocker's names. There
65 -is also a Special Page (restricted to users with the preexisting
66 -'block' ability) that allows to block, view (also view by blockers) and
67 -unblock user names and IP addresses. Current version also allows to
68 -block entire IP addresses (no range or partial blockings in this case).
69 -</dd></dl>
70 -<dl><dd> Will also include in this one an ability to prevent new
71 -account creation (yes, such a thing already exists in IP Blocks, but is
72 -not multi-wikia-wide).
73 -</dd></dl>
74 -<a name="Product_Assumptions"></a><h2>Product Assumptions</h2>
75 -<ul><li> we use a shared database (so the block is enabled on multiple wikias)
76 -</li><li> we use memcached (so database operations are sped up)
77 -</li><li> user of this special page should have the rights to block
78 -</li><li> we take care of account creation prevention multi-wikia style right here
79 -</li></ul>
80 -<a name="Product_Features"></a><h2>Product Features</h2>
81 -<dl><dd><dl><dt>Basic functionality
82 -</dt><dd> Ability to block users by their names when they try to edit
83 -pages, getting data out of the database, special page for managing
84 -blocks.
85 -</dd><dt>Extended functionality
86 -</dt><dd> Ability to block IP addresses, use of memcached, viewing blocks by blockers.
87 -</dd><dt>Additional functionality
88 -</dt><dd> Ability to block new account creation on multiple wikias,
89 -displaying efficiency statistics for particular blocks (who, when, from
90 -where tried to edit and was blocked), allowing to set expiration period
91 -for blocks (which is already done).
92 -</dd></dl>
93 -</dd></dl>
94 -<a name="Key_Product_Concepts"></a><h2>Key Product Concepts</h2>
95 -<dl><dd><dl><dt>Data fetched from shared database
96 -</dt><dd> Data should be fetched from a shared database, supported with the use of memcached instead of hardwritten from the file.
97 -</dd><dt>Submit form on one page with the list
98 -</dt><dd> Easier to manage than having them on two separated special pages.
99 -</dd><dt>May be extended by having more statistics, timestamps, allow blocks by IP ranges, and such
100 -</dt><dd> We can add some statistics into separate tables (like the
101 -reason - rather an internal note for admin, why this particular user
102 -has been blocked, or the IP's from which the user tried to login etc.).
103 -</dd></dl>
104 -</dd></dl>
105 -<p><br>
106 -</p>
107 -<a name="Product_Unknowns"></a><h2>Product Unknowns</h2>
108 -<ul><li> Should we have a similar page for usernames excluded from
109 -blocking (when we block some IPs)? This could be linked to this project
110 -(the new special page would fetch some data from this one).
111 -</li><li> Include ability to block entire IP ranges? There are already indications that it would be useful.
112 -</li><li> Include timestamps? For now, the blocks are there for infinity.
113 -<ul><li> Timestamps are now included. Expired blocks are deleted
114 -automatically when a blocked user tries to edit and an expired block is
115 -then applied. In such case, the block is discarded and user can edit.
116 -Admin can also remove expired blocks manually from list (they are
117 -denoted 'EXPIRED' in red font)
118 -</li></ul>
119 -</li><li> Show more information? Like for example statistics, which users tried to login from which IPs and how many times, etc.
120 -</li></ul>
121 -<a name="Potential_Product_Issues"></a><h2>Potential Product Issues</h2>
122 -<ul><li> As we are able to block entire IP addresses, so there is more
123 -need for a utility to allow specific users identified by their
124 -usernames to edit anyway, even when they log from the blocked address.
125 -Similar request already exists here: <a>Product_Priorities:_Ops_Upgrades_Bugs#General</a>.
126 -The special page to exclude blocks could integrate both IP Blocks and
127 -Regexblocks or it could be just two separate special pages.
128 -</li><li> <strike>The blocks are permanent at the moment. So there is more attention required to them.</strike>
129 -</li></ul>
130 -<dl><dd> Which is untrue now. The blocks can be made with a chosen
131 -expiration time. After that, then can be removed manually or on block.
132 -Either way, they will be harmless.
133 -</dd></dl>
134 -<ul><li> also a note: on older versions of MW when the user is
135 -regexblocked (s)he cannot also create accounts (same goes for normal IP
136 -blocks), on newer (I've tested it personally on 1.8.2) it allows a
137 -separate user account creation block - it's about the code, and since I
138 -was developing a clean extension, I didn't want to alter the MW code
139 -itself. This leads to a situation, when the same block of one vandal
140 -will allow him/her to create account on one wiki, and disallow on the
141 -other (assuming that they will have different versions and the option
142 -'block account creation' will be unchecked).
143 -</li></ul>
144 -<a name="Product_Interface"></a><h2>Product Interface</h2>
145 -<dl><dd> Has one Special Page named Regexblock. It displays a form, used for adding blocks, and a list, used for managing blocks.
146 -</dd></dl>
147 -<dl><dd> The form has a field, specyfing user name or IP that we want
148 -to block. While not required for a successful block, there are three
149 -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.
150 -</dd></dl>
151 -<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>.
152 -The unblock requires no confirmation and is processed instantenuosly.
153 -The list can be traversed as all standard lists in special pages (prev,
154 -next, display by 20, 50 etc.). Each row also has the link to <b>statistics</b> for a particular block.
155 -</dd></dl>
156 -<a name="Product_Structure"></a><h2>Product Structure</h2>
157 -<ul><li> <b>Files</b>
158 -<ul><li> <b>regexBlock.php</b> <i>contains includes for the files in the folder, this one replaces old regexBlock.php</i>
159 -</li><li> <b>/regexBlock</b> <i>folder containg the core of the extension plus docs</i>
160 -<ul><li> <b>README</b> <i>contains installation instructions and product description</i>
161 -</li><li> <b>regexBlockCore.php</b> <i>contains the blocking utility and use of the hook</i>
162 -</li><li> <b>SpecialRegexBlock.php</b> <i>Special Page for viewing and managing block list</i>
163 -</li><li> <b>SpecialRegexBlockStats.php</b> <i>Special Page for displaying stats for particular blocks</i>
164 -</li><li> <i> <b>SimplifiedRegex.php</b> - this one is required also by SpamRegex</i>
165 -</li></ul>
166 -</li></ul>
167 -</li></ul>
168 -<ul><li> <b>SQL</b>
169 -</li></ul>
170 -<pre>CREATE TABLE `blockedby` (
171 -`blckby_id` int(5) NOT NULL auto_increment,
172 -`blckby_name` varchar(255) character set latin1 collate latin1_bin NOT NULL,
173 -`blckby_blocker` varchar(255) character set latin1 collate latin1_bin NOT NULL,
174 -`blckby_timestamp` char(14) NOT NULL,
175 -`blckby_expire` char(14) NOT NULL,
176 -`blckby_create` tinyint(1) NOT NULL default '1',
177 -`blckby_exact` tinyint(1) NOT NULL default '0',
178 -`blckby_reason` tinyblob NOT NULL,
179 - PRIMARY KEY (`blckby_id`),
180 - UNIQUE KEY `blckby_name` (`blckby_name`),
181 - KEY `blckby_timestamp` (`blckby_timestamp`),
182 - KEY `blckby_expire` (`blckby_expire`)
183 - )&nbsp;;
184 -</pre>
185 -<pre>CREATE TABLE `stats_blockedby` (
186 -`stats_id` int(8) NOT NULL auto_increment,
187 -`stats_user` varchar(255) character set latin1 collate latin1_bin NOT NULL,
188 -`stats_blocker` varchar(255) character set latin1 collate latin1_bin NOT NULL,
189 -`stats_timestamp` char(14) NOT NULL,
190 -`stats_ip` char(15) NOT NULL,
191 -`stats_match` varchar(255) character set latin1 collate latin1_bin NOT NULL default '-',
192 -`stats_wiki` varchar(255) NOT NULL default <i>,</i>
193 - PRIMARY KEY (`stats_id`),
194 - KEY `stats_timestamp` (`stats_timestamp`)
195 - )&nbsp;;
196 -</pre>
197 -<p><br>
198 -</p>
199 -<ul><li> <b>Installation</b>
200 -</li></ul>
201 -<ol><li> Create tables in the shared database.
202 -</li><li> Copy folder /regexBlock to /extensions, copy SimplifiedRegex.php to /extensions (<b>do not</b> copy regexBlock.php yet!).
203 -</li><li> Edit regexBlock.php and set <b>REGEXBLOCK_PATH</b> to its correct value.
204 -</li><li> Copy regexBlock.php to /extensions.
205 -</li></ol>
206 -<dl><dd>That should do the trick. If people belonging to staff cannot
207 -access the extension, check if relogin helps. If not, please check if
208 -the staff permissions are not <b>overwritten</b> after the
209 -initialization of the extension. If that is the case, the permissions
210 -should be rather appended (the extension adds one new permission
211 -right).
212 -</dd></dl>
213 -<a name="Product_Use_Cases"></a><h2>Product Use Cases</h2>
214 -<ol><li> <b>Blocking a vandalising user by a username</b>
215 -</li></ol>
216 -<dl><dd><dl><dd>A user is vandalising over different wikias. An admin
217 -blocks the user by adding his/her username to the block list. The user
218 -cannot edit pages anymore until the block is released.
219 -<dl><dt>Step 1 (An admin blocks user)
220 -</dt><dd>Admin inserts the username into the database via the special page regexblock.
221 -</dd><dt>Step 2 (The user cannot edit pages)
222 -</dt><dd>Blocked user (a user that his/her username <b>contains</b> the
223 -blocked username) tries to edit. There goes a block check, and there is
224 -a match. Now, the expiration check is done. If the block is still
225 -valid, unfortunate (vandalising) user will be displayed a message that
226 -this particular user is blocked, along with the name of the blocker.
227 -Also, on a successful block, an entry into the database is made.
228 -</dd><dt>Step 3 (Admin releases the block)
229 -</dt><dd>When the situation is under control, admin can ublock the user
230 -(or not, if the user's actions made clear that the block should be
231 -permanent). Admin can also view statistics for this particular block
232 -(frequent attempts to edit despite being blocked may encourage admin to
233 -make a more permanent block...).
234 -</dd></dl>
235 -</dd></dl>
236 -</dd></dl>
237 -<ol><li> <b>Blocking a vandalising user by an IP address</b>
238 -</li></ol>
239 -<dl><dd><dl><dd> A user is vandalising over different wikias. An admin
240 -blocks the user by adding his/her IP address to the block list. No
241 -users logging from that IP will be able to edit the pages.
242 -<dl><dt>Step 1 (An admin blocks <b>full</b> IP address)
243 -</dt><dd>Admin inserts the <b>full</b> IP address into the database via the special page regexblock (<i>a
244 -partial or range IP address will be treated like a username. This is to
245 -avoid situations such as when a '3' block blocks all IP's including
246 -'3'. Regular expressions.</i>).
247 -</dd><dt>Step 2 (The user cannot edit pages)
248 -</dt><dd>When a user logging in from a particular IP address (it has to be an <b>exact</b>
249 -match) tries to edit, he/she will be displayed a message that this
250 -particular user is blocked, along with the name of the blocker. Also,
251 -all non-logged users from that IP address will be blocked.
252 -</dd><dt>Step 3 (Admin releases the block)
253 -</dt><dd>When the situation is under control, admin can ublock this IP
254 -address (or not, if the user's actions made clear that the block should
255 -be permanent).
256 -</dd></dl>
257 -</dd></dl>
258 -</dd></dl>
259 -<a name="Product_Bugs"></a><h2>Product Bugs</h2>
260 -<dl><dd><b>Known Bugs</b>:
261 -</dd></dl>
262 -<dl><dd><ul><li> there is a little glitch when someone unblocks the
263 -last block by a particular blocker (it shows 'All' on the select, but
264 -the list is empty, the only exception is when unblocked row was the
265 -last on the global list)
266 -</li></ul>
267 -</dd></dl>
268 -<a name="Discussion"></a><h2>Discussion</h2>
269 -<p>Kaz3t</a>:
270 -I'm not convinced that currently way for blocking user by name is the
271 -best. For example user name can consist of his real name and surname.
272 -So if we block user "John" we also block users "Johnny", "John Smith",
273 -etc. In my opinion we should have choice - multi-block (as it is now)
274 -and single-block (for example when we want to block this and only this
275 -specified user we put his name between quotation-marks). </p>
276 -<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)
277 -</dd><dd> I added a checkbox just for that, should be working fine. --Bartek 16:01, 20 November 2006 (UTC)
278 -</dd></dl>
279 -</dd></dl>
280 -</dd></dl>
281 -<ul><li> The message that a user sees is the same whether it is a
282 -similar name block or a specific name block (or even an IP block). I
283 -think this needs to either be made more general, or made variable. The
284 -reason set is: "This username is prevented from editing due to
285 -vandalism by a user with a similar name. Please create an alternate
286 -user name or contact Wikia about the problem"
287 -</li></ul>
288 -<dl><dd><dl><dd><dl><dd><dl><dd> There are four types of messages now
289 -(displayed to the blocked user). First, is the reason specified by an
290 -admin. If there is none, different messages will be displayed for:
291 -exact username match, IP address match and a regex username match. - --Bartek 13:07, 24 November 2006 (UTC)
292 -</dd></dl>
293 -</dd></dl>
294 -</dd></dl>
295 -</dd></dl>
296 -<ul><li> Is it possible to block <i>just</i> creation of accounts? for
297 -example to stop future creation of sannse-like accounts while still
298 -allowing me to edit? (this isn't vital though)
299 -</li></ul>
300 -<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)
301 -</dd></dl>
302 -</dd></dl>
303 -</dd></dl>
304 -</dd></dl>
305 -<ul><li> When it's an exact match block, this is mentioned on the
306 -listing. Could it also be mentioned when it is not exact? It would make
307 -this listing clearer. For example:
308 -<ul><li> Bartek (exact match) (account creation block) (blocked by: Sannse) on (etc.)
309 -</li><li> Bartek (regex match) (account creation block) (blocked by: Sannse) on (etc.)
310 -</li></ul>
311 -</li></ul>
312 -<dl><dd><dl><dd><dl><dd><dl><dd> Changed already --Bartek 09:34, 24 November 2006 (UTC)
313 -</dd></dl>
314 -</dd></dl>
315 -</dd></dl>
316 -</dd></dl>
317 -<dl><dd><dl><dd><dl><dd><dl><dd> Done - --Bartek 09:39, 24 November 2006 (UTC)
318 -</dd></dl>
319 -</dd></dl>
320 -</dd></dl>
321 -</dd></dl>
322 -<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)
323 -</li></ul>
324 -<ul><li> The list of blocks is in alphabetical order. Is this the best
325 -way? I think it may be, I'm just used to seeing such things in time
326 -order.
327 -</li></ul>
328 -<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)
329 -</dd></dl>
330 -</dd></dl>
331 -</dd></dl>
332 -</dd></dl>
333 -<ul><li> Expired blocks are shown - useful? or are they quickly going to fill up the blocklog?
334 -</li></ul>
335 -<dl><dd><dl><dd><dl><dd><dl><dd> Now they are hidden. - --Bartek 14:03, 27 November 2006 (UTC)
336 -</dd></dl>
337 -</dd></dl>
338 -</dd></dl>
339 -</dd></dl>
340 -<ul><li> All seems to work as advertised&nbsp;:) -- Sannse 11:32, 27 November 2006 (UTC)
341 -</li></ul>
342 -<ul><li> A community team question. Do we want to change the default block reasons at all? The current defaults are:
343 -<ul><li> "This IP address is prevented from editing due to vandalism" (IP block)
344 -</li><li> "This username is prevented from editing due to vandalism by
345 -a user with a similar name. Please create an alternate user name or
346 -contact Wikia about the problem" (RegEx block)
347 -</li><li> "This username is prevented from editing due to vandalism" (Username block)
348 -</li></ul>
349 -</li></ul>
350 -<dl><dd>It won't always be vandalism. Is there something more general
351 -we can use to cover vandalism, spamming, trolling, impersonation, etc? Angela 10:53, 28 November 2006 (UTC)
352 -</dd></dl>
353 -<dl><dd><dl><dd> how about "vandalism or other disruption" -- Sannse 08:59, 30 November 2006 (UTC)
354 -</dd></dl>
355 -</dd></dl>
356 -<ul><li> as Splarka said earlier (05.12.2006) on IRC - <i>an idea for
357 -blocking IP ranges: a radio box: (*) whole username (exact match) ( )
358 -regex username ( ) IP or IP range in the first: "Splarka" would block
359 -just that name... in the second "Spla" would block any username
360 -containing that... and in the third: 75.72.55.78 would block just the
361 -user's IP, and 75.72. could block 75.72.*.* simple summary: IP matching
362 -would be made manual (easier to code probably?) although CIDR support
363 -might be better than regex for IPs... eg 75.72.0.0/16</i>
364 -</li></ul>
365 -<ul><li> as Sannse and Splarka said today on IRC, it would be good to
366 -have an option to block users in case-sensitive mode. One proposition
367 -is for an exact match to be case-sensitive. - --Bartek 09:06, 18 December 2006 (UTC)
368 -</li></ul>
369 -<dl><dd><dl><dd> As agreed, it will be case-sensitive for an exact match mode. - --Bartek 09:08, 18 December 2006 (UTC)
370 -<dl><dd>Which is already under testing
371 -</dd></dl>
372 -</dd></dl>
373 -</dd></dl>
374 -<ul><li> and, as Emil said, it would be good to check if there is some
375 -proxy check within the IP check (might be already implemented in
376 -existing functions, since I am using class User's function to get the
377 -user's IP)
378 -</li></ul>
379 -<a name="Suggested_messages"></a><h2>Suggested messages</h2>
380 -<ul><li> "This IP address is prevented from editing due to vandalism or
381 -other disruption by you or by someone who shares your IP address. If
382 -you believe this is in error, please contact Wikia." (IP block)
383 -</li><li> "This username is prevented from editing due to vandalism or
384 -other disruption by a user with a similar name. Please create an
385 -alternate user name or contact Wikia about the problem." (RegEx block)
386 -</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)
387 -</li></ul>
388 -<a name="Post_release"></a><h2>Post release</h2>
389 -<p>A possible problem that's hard to confirm.
390 -User:WatchTVEatDonutDrinkBeer complained of errors on trying to access
391 -pages on wikiality. Their browser would direct them to Not Found
392 -
393 -The requested URL /Main_Page was not found on this server.
394 -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
395 -<p>Because the timing of their last edit was so similar to a block Angela had made:
396 -</p>
397 -<pre># wheels! (regex match) (account creation block) (blocked by: Angela, generic reason) on 19:43, 29 December 2006 (unblock) permanent block (stats)
398 -</pre>
399 -<p>I tried removing this block. Once I'd done that, he could access the
400 -site normally. Replacing the block had no effect. I compared his IP
401 -with the vandal's (User:Willy on wheels!) but they didn't match.
402 -Perhaps a coincidence? But recording it here just in case it isn't -- Sannse 20:42, 29 December 2006 (UTC)
403 -</p>
404 -
405 - </div>
406 - </div>
407 -
408 - <div class="visualClear"></div>
409 - <div id="footer">
410 - <div id="f-poweredbyico"><a href="http://www.mediawiki.org/"><img src="_files/poweredby_mediawiki_88x31" alt="MediaWiki"></a></div>
411 - <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>
412 - <div id="f-hosting"><i>Wikia</i> is a service mark of Wikia, Inc. All rights reserved.</div>
413 - </div>
414 -
415 -</div>
416 -
417 -</body></html>
Index: trunk/extensions/regexBlock/SpecialRegexBlockStats.php
@@ -1,113 +0,0 @@
2 -<?php
3 -
4 -/**#@+
5 - * 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
6 - *
7 - * @addtogroup SpecialPage
8 - *
9 - * @author Bartek Łapiński
10 - * @copyright Copyright © 2007, Wikia Inc.
11 - * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
12 -*/
13 -
14 -if(!defined('MEDIAWIKI'))
15 - die();
16 -
17 -/* add data to tables */
18 -$wgExtensionFunctions[] = 'wfRegexBlockStatsPageSetup';
19 -$wgExtensionCredits['specialpage'][] = array(
20 - 'name' => 'Regex Block Stats',
21 - 'author' => 'Bartek Łapiński',
22 - 'url' => 'http://www.mediawiki.org/wiki/Extension:RegexBlock',
23 - 'description' => 'Displays block statistics for the regexBlock extension',
24 - 'descriptionmsg' => 'regexblock-stat-desc',
25 -);
26 -
27 -/* special page setup function */
28 -function wfRegexBlockStatsPageSetup () {
29 - global $IP;
30 - if (!wfSimplifiedRegexCheckSharedDB())
31 - return;
32 - require_once($IP. '/includes/SpecialPage.php');
33 - /* name, restrictions, */
34 - SpecialPage::addPage(new SpecialPage('Regexblockstats', 'regexblock', false, 'wfRegexBlockStatsCore', false));
35 -}
36 -
37 -/* special page core function */
38 -function wfRegexBlockStatsCore () {
39 - global $wgOut, $wgUser, $wgRequest;
40 - $wgOut->setPageTitle (wfMsg('regexblock-stats-title'));
41 - $username = $wgRequest->getVal('target');
42 - $wgOut->setSubtitle (wfMsg('regexblock-stats-username', $username));
43 - $scL = new RegexBlockStatsList();
44 - $scL->showList('', $username);
45 -}
46 -
47 -/* list class */
48 -class RegexBlockStatsList {
49 - var $numResults;
50 -
51 - /* constructor */
52 - function RegexBlockStatsList () {
53 - $this->numResults = 0 ;
54 - }
55 -
56 - /* show it up */
57 - function showList ($error, $username) {
58 - global $wgOut, $wgRequest;
59 - /* no list when no user */
60 - if ("" == $username)
61 - return false;
62 - $this->fetchNumResults($username);
63 - $this->showPrevNext($wgOut);
64 - $wgOut->addHTML("<ul>");
65 - $this->fetchLogs($username);
66 - $wgOut->addHTML("</ul>");
67 - $this->showPrevNext($wgOut);
68 - }
69 -
70 - /* fetch number of all rows */
71 - function fetchNumResults ($username) {
72 - global $wgMemc, $wgSharedDB;
73 - $dbr = &wfGetDB (DB_SLAVE);
74 - $query_count = "SELECT COUNT(*) as n FROM ".wfRegexBlockGetStatsTable()." WHERE stats_user = ".$dbr->addQuotes ($username);
75 - $res_count = $dbr->query($query_count);
76 - $row_count = $dbr->fetchObject ($res_count);
77 - $this->numResults = $row_count->n;
78 - $dbr->freeResult ($res_count);
79 - }
80 -
81 - /* fetch all logs */
82 - function fetchLogs ($username) {
83 - global $wgOut, $wgSharedDB, $wgDBname, $wgRequest, $wgLang;
84 - /* from database */
85 - list( $limit, $offset ) = $wgRequest->getLimitOffset();
86 - $dbr =& wfGetDB (DB_SLAVE);
87 - $query = "SELECT stats_user, stats_blocker, stats_timestamp, stats_ip
88 - FROM ".wfRegexBlockGetStatsTable()."
89 - WHERE stats_user={$dbr->addQuotes($username)}
90 - ORDER BY stats_timestamp DESC LIMIT $offset,$limit";
91 - $res = $dbr->query($query);
92 - while ($row = $dbr->fetchObject($res)) {
93 - $time = $wgLang->timeanddate( wfTimestamp( TS_MW, $row->stats_timestamp ), true );
94 - $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>");
95 - }
96 - $dbr->freeResult($res);
97 - }
98 -
99 - /* init for showprevnext */
100 - function showPrevNext( &$out ) {
101 - global $wgContLang, $wgRequest;
102 - list( $limit, $offset ) = $wgRequest->getLimitOffset();
103 - $target = 'target=' . urlencode ( $wgRequest->getVal('target') );
104 - $mode = '&mode=' . urlencode ( $wgRequest->getVal('mode') );
105 - $html = wfViewPrevNext(
106 - $offset,
107 - $limit,
108 - $wgContLang->specialpage( 'Regexblockstats' ),
109 - $target.$mode,
110 - ($this->numResults - $offset) <= $limit
111 - );
112 - $out->addHTML( '<p>' . $html . '</p>' );
113 - }
114 -}
\ No newline at end of file
Index: trunk/extensions/regexBlock/patch-upgrade.sql
@@ -0,0 +1,4 @@
 2+ALTER TABLE stats_blockedby ADD COLUMN `stats_blckby_id` int(8) NOT NULL;
 3+ALTER TABLE stats_blockedby ADD COLUMN `stats_match` varchar(255) NOT NULL default '';
 4+ALTER TABLE stats_blockedby ADD COLUMN `stats_dbname` varchar(255) NOT NULL default '';
 5+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
___________________________________________________________________
Added: svn:eol-style
16 + native
Index: trunk/extensions/regexBlock/regexBlockCore.php
@@ -1,234 +1,605 @@
22 <?php
3 -/**#@+
 3+/**
44 * 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
55 *
6 - * @addtogroup SpecialPage
7 - *
8 - * @author Bartek Łapiński
 6+ * @file
 7+ * @ingroup Extensions
 8+ * @author Bartek Łapiński <bartek(at)wikia-inc.com>
 9+ * @author Piotr Molski <moli@wikia-inc.com>
 10+ * @author Adrian 'ADi' Wieczorek <adi(at)wikia-inc.com>
911 * @copyright Copyright © 2007, Wikia Inc.
1012 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
1113 */
1214
13 -/* add hook */
14 -global $wgHooks;
15 -$wgHooks['GetBlockedStatus'][] = 'wfRegexBlockCheck';
 15+/**
 16+ * Prepare data by getting blockers
 17+ * @param $current_user User: current user
 18+ */
 19+function wfRegexBlockCheck( $current_user ) {
 20+ wfProfileIn( __METHOD__ );
1621
17 -/*
18 - prepare data by getting blockers
19 - @param $current_user User: current user
20 -*/
21 -function wfRegexBlockCheck ($current_user) {
22 - global $wgMemc, $wgSharedDB;
23 - if (!wfSimplifiedRegexCheckSharedDB())
 22+ $ip_to_check = wfGetIP();
 23+
 24+ /* First check cache */
 25+ $blocked = wfRegexBlockIsBlockedCheck( $current_user, $ip_to_check );
 26+ if ( $blocked ) {
 27+ wfProfileOut( __METHOD__ );
2428 return true;
25 - $ip_to_check = wfGetIP();
26 - $key = "$wgSharedDB:regexBlockCore:blockers";
27 - $cached = $wgMemc->get($key);
28 - if (!is_array($cached)) {
 29+ }
 30+ $blockers_array = wfRegexBlockGetBlockers();
 31+ $block_data = wfGetRegexBlockedData( $current_user, $blockers_array );
 32+
 33+ /* check user for each blocker */
 34+ foreach( $blockers_array as $blocker ) {
 35+ $blocker_block_data = isset( $block_data[$blocker] ) ? $block_data[$blocker] : null;
 36+ wfGetRegexBlocked( $blocker, $blocker_block_data, $current_user, $ip_to_check );
 37+ }
 38+
 39+ wfProfileOut( __METHOD__ );
 40+ return true;
 41+}
 42+
 43+/**
 44+ * Get blockers
 45+ */
 46+function wfRegexBlockGetBlockers( $master = 0 ) {
 47+ global $wgSharedDB;
 48+ $oMemc = wfGetCache( CACHE_MEMCACHED );
 49+ wfProfileIn( __METHOD__ );
 50+
 51+ $key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_BLOCKERS_KEY );
 52+ $cached = $oMemc->get($key);
 53+ $blockers_array = array();
 54+
 55+ if ( !is_array( $cached ) ) {
2956 /* get from database */
30 - $blockers_array = array();
31 - $dbr =& wfGetDB (DB_SLAVE);
32 - $query = "SELECT blckby_blocker FROM ".wfRegexBlockGetTable()." GROUP BY blckby_blocker";
33 - $res = $dbr->query($query);
34 - while ( $row = $dbr->fetchObject( $res ) ) {
35 - wfGetRegexBlocked ($row->blckby_blocker, $current_user, $ip_to_check);
36 - array_push ($blockers_array, $row->blckby_blocker);
 57+ $dbr = wfGetDB( ( empty( $master ) ) ? DB_SLAVE : DB_MASTER );
 58+ $oRes = $dbr->select( REGEXBLOCK_TABLE,
 59+ array("blckby_blocker"),
 60+ array("blckby_blocker <> ''"),
 61+ __METHOD__,
 62+ array("GROUP BY" => "blckby_blocker")
 63+ );
 64+ while( $oRow = $dbr->fetchObject($oRes) ) {
 65+ $blockers_array[] = $oRow->blckby_blocker;
3766 }
38 - $dbr->freeResult($res);
39 - $wgMemc->set($key, $blockers_array, REGEXBLOCK_EXPIRE);
 67+ $dbr->freeResult($oRes);
 68+ $oMemc->set( $key, $blockers_array, REGEXBLOCK_EXPIRE );
4069 } else {
4170 /* get from cache */
42 - foreach ($cached as $blocker) {
43 - wfGetRegexBlocked ($blocker, $current_user, $ip_to_check);
44 - }
 71+ $blockers_array = $cached;
4572 }
46 - return true;
 73+
 74+ wfProfileOut( __METHOD__ );
 75+ return $blockers_array;
4776 }
4877
49 -/*
50 - fetch usernames or IP addresses to run a match against
51 - @param $blocker String: the admin who blocked
52 - @param $user User: current user
53 - @param $mode integer: REGEXBLOCK_MODE_IPS or REGEXBLOCK_MODE_NAMES
54 - @return String: string to run a regex match against
55 -*/
56 -function wfGetRegexBlockedData ($blocker, $user, $mode) {
57 - global $wgMemc, $wgUser, $wgSharedDB;
58 - $names = "";
59 - $first = true;
 78+/**
 79+ * Check if user is blocked
 80+ *
 81+ * @param $user User
 82+ * @param $ip
 83+ * @return Array: an array of arrays to run a regex match against
 84+ */
 85+function wfRegexBlockIsBlockedCheck( $user, $ip ) {
 86+ global $wgSharedDB;
 87+ $oMemc = wfGetCache( CACHE_MEMCACHED );
6088
61 - /* first, check if regex string is already stored in memcache */
62 - $key = str_replace( " ", "_", "$wgSharedDB:regexBlockCore:$mode:blocker:$blocker" );
63 - $cached = $wgMemc->get($key);
64 - if ( "" == $cached ) {
65 - /* fetch data from db, concatenate into one string, then fill cache */
66 - $dbr =& wfGetDB( DB_SLAVE );
67 - $query = "SELECT blckby_name, blckby_exact FROM ".wfRegexBlockGetTable()." WHERE blckby_blocker = {$dbr->addQuotes($blocker)}";
68 - $res = $dbr->query($query);
69 - while ( $row = $dbr->fetchObject( $res ) ) {
70 - $concat = "";
71 - $is_ip = $user->isIP($row->blckby_name);
72 - /* IPs are checked in exact mode, marked as exact also */
73 - $simplified = $row->blckby_name;
74 - if (( (REGEXBLOCK_MODE_IPS == $mode) && ($is_ip != 0) ) || $row->blckby_exact) {
75 - $concat = "^{$simplified}$";
76 - } else if ((REGEXBLOCK_MODE_NAMES == $mode)) {
77 - $concat = $simplified;
 89+ wfProfileIn( __METHOD__ );
 90+ $result = false;
 91+
 92+ $key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_USER_KEY, str_replace( ' ', '_', $user->getName() ) );
 93+ $cached = $oMemc->get( $key );
 94+
 95+ if ( is_object( $cached ) ) {
 96+ $ret = wfRegexBlockExpireNameCheck( $cached );
 97+ if ( ( $ret !== false ) && ( is_array( $ret ) ) ) {
 98+ $ret['match'] = $user->getName();
 99+ $ret['ip'] = 0;
 100+ $result = wfRegexBlockSetUserData( $user, $ip, $ret['blocker'], $ret );
 101+ }
 102+ }
 103+
 104+ if ( ( $result === false ) && ( $ip != $user->getName() ) ) {
 105+ $key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_USER_KEY, str_replace( ' ', '_', $ip ) );
 106+ $cached = $oMemc->get( $key );
 107+ if ( is_object( $cached ) ) {
 108+ $ret = wfRegexBlockExpireNameCheck( $cached );
 109+ if ( ( $ret !== false ) && ( is_array( $ret ) ) ) {
 110+ $ret['match'] = $ip;
 111+ $ret['ip'] = 1;
 112+ $result = wfRegexBlockSetUserData($user, $ip, $ret['blocker'], $ret);
78113 }
79 - if ($concat != "") {
80 - if (!$first) {
81 - $names .= "|".$concat;
82 - } else {
83 - $names .= $concat;
84 - $first = false;
 114+ }
 115+ }
 116+
 117+ wfProfileOut( __METHOD__ );
 118+ return $result;
 119+}
 120+
 121+function wfRegexBuildExpression( $lines, $exact = 0, $batchSize = 4096 ) {
 122+ global $useSpamRegexNoHttp;
 123+
 124+ wfProfileIn( __METHOD__ );
 125+ /* Make regex */
 126+ $regexes = array();
 127+ $regexStart = ($exact) ? '/^(' : '/(';
 128+ $regexEnd = '';
 129+ if ( !empty( $exact ) ) {
 130+ $regexEnd = ')$/';
 131+ } elseif ( $batchSize > 0 ) {
 132+ $regexEnd = ')/Si';
 133+ } else {
 134+ $regexEnd = ')/i';
 135+ }
 136+ $build = false;
 137+ foreach( $lines as $line ) {
 138+ if( $build == '' ) {
 139+ $build = $line;
 140+ } elseif( strlen( $build ) + strlen( $line ) > $batchSize ) {
 141+ $regexes[] = /*$regexStart . */str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $build) ) /*. $regexEnd*/;
 142+ $build = $line;
 143+ } else {
 144+ $build .= '|';
 145+ $build .= $line;
 146+ }
 147+ }
 148+
 149+ if( $build !== false ) {
 150+ $regexes[] = /*$regexStart . */str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $build) ) /*. $regexEnd*/;
 151+ }
 152+
 153+ wfProfileOut( __METHOD__ );
 154+ return $regexes;
 155+}
 156+
 157+function wfRegexIsCorrectCacheValue( $cached ) {
 158+ $result = false;
 159+ if ( empty( $cached ) ) {
 160+ $result = true;
 161+ } else {
 162+ $loop = 0;
 163+ $names = array('ips' => '', 'exact' => '', 'regex' => '');
 164+ foreach( $names as $key => $value ) {
 165+ if ( array_key_exists($key, $cached) && ( !empty( $cached[$key] ) ) ) {
 166+ $loop++;
 167+ }
 168+ }
 169+ if ( $loop == 0 ) {
 170+ $result = true;
 171+ }
 172+ }
 173+ return $result;
 174+}
 175+
 176+/**
 177+ * Fetch usernames or IP addresses to run a match against
 178+ *
 179+ * @param $blocker String: the admin who blocked
 180+ * @param $user User: current user
 181+ * @return Array: an array of arrays to run a regex match against
 182+ */
 183+function wfGetRegexBlockedData( $blocker, $user, $master = 0 ) {
 184+ global $wgSharedDB;
 185+ $oMemc = wfGetCache( CACHE_MEMCACHED );
 186+
 187+ wfProfileIn( __METHOD__ );
 188+ $blockData = array();
 189+
 190+ /**
 191+ * First, check if regex strings are already stored in memcached
 192+ * we will store entire array of regex strings here
 193+ */
 194+ if ( !( $user instanceof User ) ) {
 195+ wfProfileOut( __METHOD__ );
 196+ return false;
 197+ }
 198+
 199+ $memkey = wfForeignMemcKey( ( isset( $wgSharedDB)) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_BLOCKERS_KEY, "All-In-One" );
 200+ $cached = $oMemc->get( $memkey );
 201+
 202+ if ( empty( $cached ) ) {
 203+ /* Fetch data from DB, concatenate into one string, then fill cache */
 204+ $dbr = wfGetDB( ( empty( $master ) ) ? DB_SLAVE : DB_MASTER );
 205+
 206+ foreach( $blockers as $blocker ) {
 207+ $oRes = $dbr->select(
 208+ REGEXBLOCK_TABLE,
 209+ array("blckby_id", "blckby_name", "blckby_exact"),
 210+ array("blckby_blocker = {$dbr->addQuotes($blocker)}"),
 211+ __METHOD__
 212+ );
 213+
 214+ $loop = 0;
 215+ $names = array( 'ips' => '', 'exact' => '', 'regex' => '' );
 216+ while ( $oRow = $dbr->fetchObject( $oRes ) ) {
 217+ $key = 'regex';
 218+ if ( $user->isIP($oRow->blckby_name) != 0 ) {
 219+ $key = 'ips';
 220+ } elseif ( $oRow->blckby_exact != 0 ) {
 221+ $key = 'exact';
85222 }
 223+ $names[$key][] = $oRow->blckby_name;
 224+ $loop++;
86225 }
 226+ $dbr->freeResult($oRes);
 227+
 228+ if ( $loop > 0 ) {
 229+ $blockData[$blocker] = $names;
 230+ }
87231 }
88 - $wgMemc->set($key, $names, REGEXBLOCK_EXPIRE);
89 - $dbr->freeResult($res);
 232+ $oMemc->set( $memkey, $blockData, REGEXBLOCK_EXPIRE );
90233 } else {
91 - /* take from cache */
92 - $names = $cached;
93 - }
94 - return $names;
 234+ /* take it from cache */
 235+ $blockData = $cached;
 236+ }
 237+
 238+ wfProfileOut( __METHOD__ );
 239+ return $blockData;
95240 }
96241
97 -/*
98 - check if the block expired or not (AFTER we found an existing block)
99 - @param $user User: current user object
100 - @param $names Array: matched names
101 - @param $ips Array: matched ips
102 - @return Array or false
103 -*/
104 -function wfRegexBlockExpireCheck ($user, $names = null, $ips = null) {
105 - global $wgMemc, $wgSharedDB;
106 - /* I will use memcache, with the key being particular block */
107 - if ( is_array ($ips) ) {
108 - $array_match = $ips;
109 - $username = wfGetIP();
110 - } else {
111 - $array_match = $names;
112 - $username = $user->getName();
 242+/**
 243+ * Perform a match against all given values
 244+ *
 245+ * @param $matching Array: array of strings containing list of values
 246+ * @param $value String: a given value to run a match against
 247+ * @param $exact Boolean: whether or not perform an exact match
 248+ * @return Array of matched values or false
 249+ */
 250+function wfRegexBlockPerformMatch( $matching, $value ) {
 251+ wfProfileIn( __METHOD__ );
 252+ $matched = array();
 253+
 254+ if ( !is_array( $matching ) ) {
 255+ /* empty? begone! */
 256+ wfProfileOut( __METHOD__ );
 257+ return false;
113258 }
 259+
 260+ /* normalise for regex */
 261+ $loop = 0;
 262+ $match = array();
 263+ foreach( $matching as $one ) {
 264+ /* the real deal */
 265+ $found = preg_match('/'.$one.'/i', $value, $match);
 266+ if ( $found ) {
 267+ if ( is_array( $match ) && ( !empty( $match[0] ) ) ) {
 268+ $matched[] = $one;
 269+ break;
 270+ }
 271+ }
 272+ }
 273+
 274+ wfProfileOut( __METHOD__ );
 275+ return $matched;
 276+}
 277+
 278+/**
 279+ * Check if the block expired or not (AFTER we found an existing block)
 280+ *
 281+ * @param $user User: current user object
 282+ * @param $array_match Boolean
 283+ * @param $names Array: matched names
 284+ * @param $ips Array: matched ips
 285+ * @return Array or false
 286+ */
 287+function wfRegexBlockExpireCheck( $user, $array_match = null, $ips = 0, $iregex = 0 ) {
 288+ global $wgSharedDB;
 289+ $oMemc = wfGetCache( CACHE_MEMCACHED );
 290+ wfProfileIn( __METHOD__ );
 291+ /* I will use memcached, with the key being particular block */
 292+ if ( empty( $array_match ) ) {
 293+ wfProfileOut( __METHOD__ );
 294+ return false;
 295+ }
 296+
114297 $ret = array();
115 - $dbr =& wfGetDB (DB_SLAVE);
116 - /* for EACH match check whether timestamp expired until found VALID timestamp
117 - but: only for a BLOCKED user, and it will be memcached
118 - moreover, expired blocks will be consequently deleted
119 - */
120 - foreach ($array_match as $single) {
121 - $key = str_replace( " ", "_", "$wgSharedDB:regexBlockCore:blocked:$single" );
122 - $cached = $wgMemc->get($key);
123 - if ( !is_object ($cached) ) {
 298+ /**
 299+ * For EACH match check whether timestamp expired until found VALID timestamp
 300+ * but: only for a BLOCKED user, and it will be memcached
 301+ * moreover, expired blocks will be consequently deleted
 302+ */
 303+ $blocked = '';
 304+ foreach( $array_match as $single ) {
 305+ $key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_USER_KEY, str_replace( ' ', '_', $single ) );
 306+ $blocked = null;
 307+ $cached = $oMemc->get( $key );
 308+ if ( empty( $cached ) || ( !is_object( $cached ) ) ) {
124309 /* get from database */
125 - $query = "SELECT blckby_timestamp, blckby_expire, blckby_blocker, blckby_create, blckby_exact, blckby_reason
126 - FROM ".wfRegexBlockGetTable()."
127 - WHERE blckby_name like {$dbr->addQuotes('%'.$single.'%')}";
128 -
129 - $res = $dbr->query($query);
130 - if ($row = $dbr->fetchObject ($res) ) {
 310+ $dbr = wfGetDB( DB_MASTER );
 311+ $where = array("blckby_name LIKE '%{$single}%'");
 312+ if ( !empty($iregex) ) {
 313+ $where = array("blckby_name = " . $dbr->addQuotes($single));
 314+ }
 315+ $oRes = $dbr->select( REGEXBLOCK_TABLE,
 316+ array("blckby_id", "blckby_timestamp", "blckby_expire", "blckby_blocker", "blckby_create", "blckby_exact", "blckby_reason"),
 317+ $where,
 318+ __METHOD__
 319+ );
 320+ if ( $oRow = $dbr->fetchObject( $oRes ) ) {
131321 /* if still valid or infinite, ok to block user */
132 - if ((wfTimestampNow () <= $row->blckby_expire) || ('infinite' == $row->blckby_expire)) {
133 - $ret['create'] = $row->blckby_create;
134 - $ret['exact'] = $row->blckby_exact;
135 - $ret['reason'] = $row->blckby_reason;
136 - $wgMemc->set($key, $row);
137 - $dbr->freeResult($res);
138 - return $ret;
139 - } else { /* clean up an obsolete block */
140 - wfRegexBlockClearExpired ($single, $row->blckby_blocker);
141 - }
 322+ $blocked = $oRow;
142323 }
143 - $dbr->freeResult($res);
 324+ $dbr->freeResult ($oRes);
144325 } else {
145326 /* get from cache */
146 - if ((wfTimestampNow () <= $cached->blckby_expire) || ('infinite' == $cached->blckby_expire)) {
147 - $ret['create'] = $cached->blckby_create;
148 - $ret['exact'] = $cached->blckby_exact;
149 - $ret['reason'] = $cached->blckby_reason;
 327+ $blocked = $cached;
 328+ }
 329+
 330+ /* check conditions */
 331+ if ( is_object($blocked) ) {
 332+ $ret = wfRegexBlockExpireNameCheck($blocked);
 333+ if ( $ret !== false ) {
 334+ $ret['match'] = $single;
 335+ $ret['ip'] = $ips;
 336+ $oMemc->set($key, $blocked);
 337+ wfProfileOut( __METHOD__ );
150338 return $ret;
151 - } else { /* clean up an obsolete block */
152 - wfRegexBlockClearExpired ($single, $cached->blckby_blocker);
 339+ } else {
 340+ /* clean up an obsolete block */
 341+ wfRegexBlockClearExpired($single, $blocked->blckby_blocker);
153342 }
154343 }
155344 }
 345+
 346+ wfProfileOut( __METHOD__ );
156347 return false;
157348 }
158349
159 -/* clean up an existing expired block
160 - @param $username String: name of the user
161 - @param $blocker String: name of the blocker
 350+/**
 351+ * Check if the USER block expired or not (AFTER we found an existing block)
 352+ *
 353+ * @param $blocked: block object
 354+ * @return Array or false
 355+ */
 356+function wfRegexBlockExpireNameCheck( $blocked ) {
 357+ $ret = false;
 358+ wfProfileIn( __METHOD__ );
 359+ if( is_object($blocked) ) {
 360+ if ( (wfTimestampNow () <= $blocked->blckby_expire) || ('infinite' == $blocked->blckby_expire) ) {
 361+ $ret = array(
 362+ 'blckid' => $blocked->blckby_id,
 363+ 'create' => $blocked->blckby_create,
 364+ 'exact' => $blocked->blckby_exact,
 365+ 'reason' => $blocked->blckby_reason,
 366+ 'expire' => $blocked->blckby_expire,
 367+ 'blocker'=> $blocked->blckby_blocker,
 368+ 'timestamp' => $blocked->blckby_timestamp
 369+ );
 370+ }
 371+ }
 372+ wfProfileOut( __METHOD__ );
 373+ return $ret;
 374+}
162375
163 -*/
164 -function wfRegexBlockClearExpired ($username, $blocker) {
165 - $dbw =& wfGetDB( DB_MASTER );
166 - $query = "DELETE FROM ".wfRegexBlockGetTable()." WHERE blckby_name = ".$dbw->addQuotes($username);
167 - $dbw->query($query);
 376+/**
 377+ * Clean up an existing expired block
 378+ *
 379+ * @param $username String: name of the user
 380+ * @param $blocker String: name of the blocker
 381+ */
 382+function wfRegexBlockClearExpired( $username, $blocker ) {
 383+ wfProfileIn( __METHOD__ );
 384+ $result = false;
 385+
 386+ $dbw = wfGetDB( DB_MASTER );
 387+
 388+ $dbw->delete( REGEXBLOCK_TABLE,
 389+ array("blckby_name = {$dbw->addQuotes($username)}"),
 390+ __METHOD__
 391+ );
 392+
168393 if ( $dbw->affectedRows() ) {
169394 /* success, remember to delete cache key */
170 - wfRegexBlockUnsetKeys ($blocker, $username);
171 - return true;
 395+ wfRegexBlockUnsetKeys( $username );
 396+ $result = true;
172397 }
173 - return false;
 398+
 399+ wfProfileOut( __METHOD__ );
 400+ return $result;
174401 }
175402
176 -/* put the stats about block into database
177 - @param $username String
178 - @param $user_ip String: IP of the current user
179 - @param $blocker String
180 -*/
181 -function wfRegexBlockUpdateStats ($username, $user_ip, $blocker) {
182 - global $wgSharedDB;
183 - $dbw =& wfGetDB( DB_MASTER );
184 - $now = wfTimestampNow();
185 - $query = "INSERT INTO ".wfRegexBlockGetStatsTable()."
186 - (stats_id, stats_user, stats_ip, stats_blocker, stats_timestamp)
187 - values (null, {$dbw->addQuotes($username)}, '{$user_ip}',{$dbw->addQuotes($blocker)},'{$now}')";
188 - $res = $dbw->query($query);
189 - if ( $dbw->affectedRows() ) {
190 - return true;
 403+/**
 404+ * Put the stats about block into database
 405+ *
 406+ * @param $username String
 407+ * @param $user_ip String: IP of the current user
 408+ * @param $blocker String
 409+ * @param $match
 410+ * @param $blckid
 411+ */
 412+function wfRegexBlockUpdateStats( $user, $user_ip, $blocker, $match, $blckid ) {
 413+ global $wgSharedDB, $wgDBname;
 414+
 415+ $result = false;
 416+ wfProfileIn( __METHOD__ );
 417+
 418+ $dbw = wfGetDB( DB_MASTER );
 419+ $dbw->insert( REGEXBLOCK_STATS_TABLE,
 420+ array(
 421+ 'stats_id' => 'null',
 422+ 'stats_blckby_id' => $blckid,
 423+ 'stats_user' => $user->getName(),
 424+ 'stats_ip' => $user_ip,
 425+ 'stats_blocker' => $blocker,
 426+ 'stats_timestamp' => wfTimestampNow(),
 427+ 'stats_match' => $match,
 428+ 'stats_dbname' => $wgDBname
 429+ ),
 430+ __METHOD__
 431+ );
 432+
 433+ if ( $dbw->affectedRows() ) {
 434+ $result = true;
191435 }
192 - return false;
 436+
 437+ wfProfileOut( __METHOD__ );
 438+ return $result;
193439 }
194440
195 -/*
196 - the actual blocking goes here, for each blocker
197 - @param $blocker String
198 - @param $user User
199 - @param $user_ip String
200 -*/
201 -function wfGetRegexBlocked ($blocker, $user, $user_ip) {
202 - global $wgContactLink;
203 - $names = wfGetRegexBlockedData ($blocker, $user, REGEXBLOCK_MODE_NAMES);
204 - $ips = wfGetRegexBlockedData ($blocker, $user, REGEXBLOCK_MODE_IPS);
205 - $username = $user->getName();
206 - $matched_name = preg_match ('/'.$names.'/i', $user->getName(), $matches);
207 - $matched_ip = preg_match ('/'.$ips.'/i', $user_ip, $ip_matches );
 441+/**
 442+ * The actual blocking goes here, for each blocker
 443+ *
 444+ * @param $blocker String
 445+ * @param $blocker_block_data
 446+ * @param $user User
 447+ * @param $user_ip String
 448+ */
 449+function wfGetRegexBlocked( $blocker, $blocker_block_data, $user, $user_ip ) {
 450+ wfProfileIn( __METHOD__ );
208451
209 - if ( ( $matched_name && ($names != "") ) || ( $matched_ip && ($ips != "") ) ) {
 452+ if( $blocker_block_data == null ) {
 453+ // no data for given blocker, aborting...
 454+ wfProfileOut( __METHOD__ );
 455+ return false;
 456+ }
 457+
 458+ $ips = isset($blocker_block_data['ips']) ? $blocker_block_data['ips'] : null;
 459+ $names = isset($blocker_block_data['regex']) ? $blocker_block_data['regex'] : null;
 460+ $exact = isset($blocker_block_data['exact']) ? $blocker_block_data['exact'] : null;
 461+ // backward compatibility ;)
 462+ $result = $blocker_block_data;
 463+
 464+ /* check IPs */
 465+ if ( (!empty($ips)) && (in_array($user_ip, $ips)) ) {
 466+ $result['ips']['matches'] = array($user_ip);
 467+ wfDebugLog('RegexBlock', "Found some IPs to block: ". implode(",", $result['ips']['matches']). "\n");
 468+ }
 469+
 470+ /* check regexes */
 471+ if ( ( !empty( $result['regex'] ) ) && ( is_array( $result['regex'] ) ) ) {
 472+ $result['regex']['matches'] = wfRegexBlockPerformMatch( $result['regex'], $user->getName() );
 473+ if( !empty( $result['regex']['matches'] ) ) {
 474+ wfDebugLog('RegexBlock', "Found some regexes to block: ". implode(",", $result['regex']['matches']). "\n");
 475+ }
 476+ }
 477+
 478+ /* check names of user */
 479+ $exact = ( is_array( $exact ) ) ? $exact : array($exact);
 480+ if ( ( !empty( $exact ) ) && ( in_array( $user->getName(), $exact ) ) ) {
 481+ $key = array_search( $user->getName(), $exact );
 482+ $result['exact']['matches'] = array($exact[$key]);
 483+ wfDebugLog('RegexBlock', "Found some users to block: ". implode(",", $result['exact']['matches']). "\n");
 484+ }
 485+
 486+ unset($ips);
 487+ unset($names);
 488+ unset($exact);
 489+
 490+ /**
 491+ * Run expire checks for all matched values
 492+ * this is only for determining validity of this block, so
 493+ * a first successful match means the block is applied
 494+ */
 495+ $valid = false;
 496+ foreach( $result as $key => $value ) {
 497+ $is_ip = ("ips" == $key) ? 1 : 0;
 498+ $is_regex = ("regex" == $key) ? 1 : 0;
210499 /* check if this block hasn't expired already */
211 - if ($matched_ip && ($ips != "")) {
212 - $valid = wfRegexBlockExpireCheck ($user, null, $ip_matches);
213 - } else {
214 - $valid = wfRegexBlockExpireCheck ($user, $matches, null);
 500+ if ( !empty( $result[$key]['matches'] ) ) {
 501+ $valid = wfRegexBlockExpireCheck( $user, $result[$key]['matches'], $is_ip, $is_regex );
 502+ if ( is_array( $valid ) ) {
 503+ break;
 504+ }
215505 }
216 - if ( is_array ($valid) ) {
217 - $user->mBlockedby = $blocker ;
218 - if ($valid['reason'] != "") { /* a reason was given, display it */
219 - $user->mBlockreason = $valid['reason'];
220 - } else { /* display generic reasons */
221 - if ($matched_ip && ($ips != "") ) { /* we blocked by IP */
222 - $user->mBlockreason = wfMsgHtml('regexblock-reason-ip', $wgContactLink);
223 - } else if ($valid['exact'] == 1) { /* we blocked by username exact match */
224 - $user->mBlockreason = wfMsgHtml('regexblock-reason-name', $wgContactLink);
225 - } else { /* we blocked by regex match */
226 - $user->mBlockreason = wfMsgHtml('regexblock-reason-regex', $wgContactLink);
227 - }
 506+ }
 507+
 508+ if ( is_array( $valid ) ) {
 509+ wfRegexBlockSetUserData( $user, $user_ip, $blocker, $valid );
 510+ }
 511+
 512+ wfProfileOut( __METHOD__ );
 513+ return true;
 514+}
 515+
 516+/**
 517+ * Update user structure
 518+ *
 519+ * @param $user User
 520+ * @param $user_ip String
 521+ * @param $blocker String
 522+ * @param $valid: blocked info
 523+ */
 524+function wfRegexBlockSetUserData( &$user, $user_ip, $blocker, $valid ) {
 525+ global $wgContactLink;
 526+ wfProfileIn( __METHOD__ );
 527+ $result = false;
 528+
 529+ if ( !( $user instanceof User ) ) {
 530+ wfProfileOut( __METHOD__ );
 531+ return $result;
 532+ }
 533+
 534+ wfLoadExtensionMessages( 'RegexBlock' );
 535+
 536+ if ( empty( $wgContactLink ) ) {
 537+ $wgContactLink = '[[Special:Contact|contact us]]';
 538+ }
 539+
 540+ if ( is_array( $valid ) ) {
 541+ $user->mBlockedby = User::idFromName($blocker);
 542+ if ( $valid['reason'] != '' ) {
 543+ /* a reason was given, display it */
 544+ $user->mBlockreason = $valid['reason'];
 545+ } else {
 546+ /**
 547+ * Display generic reasons
 548+ * By default we blocked by regex match
 549+ */
 550+ $user->mBlockreason = wfMsg( 'regexblock-reason-regex', $wgContactLink );
 551+ if ( $valid['ip'] == 1 ) {
 552+ /* we blocked by IP */
 553+ $user->mBlockreason = wfMsg( 'regexblock-reason-ip', $wgContactLink );
 554+ } else if ($valid['exact'] == 1) {
 555+ /* we blocked by username exact match */
 556+ $user->mBlockreason = wfMsg( 'regexblock-reason-name', $wgContactLink );
228557 }
229 - /* account creation check goes through the same hook... */
230 - if ($valid['create'] == 1) $user->mBlock->mCreateAccount = 1;
 558+ }
 559+ /* account creation check goes through the same hook... */
 560+ if ( $valid['create'] == 1 ) {
 561+ if ( $user->mBlock ) {
 562+ $user->mBlock->mCreateAccount = 1;
 563+ }
 564+ }
 565+ /* set expiry information */
 566+ if ( $user->mBlock ) {
 567+ $user->mBlock->mId = $valid['blckid'];
 568+ $user->mBlock->mExpiry = $valid['expire'];
 569+ $user->mBlock->mTimestamp = $valid['timestamp'];
 570+ $user->mBlock->mAddress = ($valid['ip'] == 1) ? wfGetIP() : $user->getName();
 571+ }
231572
232 - wfRegexBlockUpdateStats ($username, $user_ip, $blocker);
233 - }
 573+ $result = wfRegexBlockUpdateStats( $user, $user_ip, $blocker, $valid['match'], $valid['blckid'] );
234574 }
 575+
 576+ wfProfileOut( __METHOD__ );
 577+ return $result;
235578 }
 579+
 580+/**
 581+ * Clean the memcached keys
 582+ *
 583+ * @param $username name of username
 584+ */
 585+function wfRegexBlockUnsetKeys( $username ) {
 586+ global $wgSharedDB, $wgUser;
 587+ wfProfileIn( __METHOD__ );
 588+
 589+ $readMaster = 1;
 590+ $oMemc = wfGetCache( CACHE_MEMCACHED );
 591+ $key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_SPECIAL_KEY, REGEXBLOCK_SPECIAL_NUM_RECORD );
 592+ $oMemc->delete( $key );
 593+ /* main cache of user-block data */
 594+ $key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_USER_KEY, str_replace( ' ', '_', $username ) );
 595+ $oMemc->delete( $key );
 596+ /* blockers */
 597+ $key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_BLOCKERS_KEY );
 598+ $oMemc->delete( $key );
 599+ $blockers_array = wfRegexBlockGetBlockers( $readMaster );
 600+ /* blocker's matches */
 601+ $key = wfForeignMemcKey( ( isset( $wgSharedDB ) ) ? $wgSharedDB : 'wikicities', '', REGEXBLOCK_BLOCKERS_KEY, "All-In-One" );
 602+ $oMemc->delete( $key );
 603+ wfGetRegexBlockedData( $wgUser, $blockers_array, $readMaster );
 604+
 605+ wfProfileOut( __METHOD__ );
 606+}
\ No newline at end of file
Index: trunk/extensions/regexBlock/regexBlock.i18n.php
@@ -1,94 +1,77 @@
22 <?php
33 /**
4 - * Internationalisation file for extension regexBlock.
 4+ * Internationalisation file for regexBlock extension.
55 *
6 - * @addtogroup Extensions
7 -*/
 6+ * @file
 7+ * @ingroup Extensions
 8+ */
89
910 $messages = array();
1011
 12+/** English
 13+ * @author Bartek Łapiński
 14+ * @author Piotr Molski
 15+ * @author Tomasz Klim
 16+ */
1117 $messages['en'] = array(
12 - 'regexblock' => 'Regex block',
13 - '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',
14 - 'regexblock-special-desc' => 'alternate user block (by given name, using regular expressions)',
15 - 'regexblock-stat-desc' => 'Displays [[Special:Regexblockstats|block statistics]] for the regexblock extension',
16 - 'regexblock-page-title' => 'Regular expression name block',
17 - 'regexblockstats' => 'Regex block statistics',
18 - '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.
19 -If you believe this is in error, please $1' ,
20 - 'regexblock-reason-name' => 'This username is prevented from editing due to vandalism or other disruption.
21 -If you believe this is in error, please $1',
22 - 'regexblock-reason-regex' => 'This username is prevented from editing due to vandalism or other disruption by a user with a similar name.
23 -Please create an alternate user name or $1 about the problem',
24 - 'regexblock-help' => 'Use the form below to block write access from a specific IP address or username.
 18+ 'regexblock' => 'Regex block',
 19+ 'regexblock-already-blocked' => '$1 is already blocked.',
 20+ 'regexblock-block-log' => 'User name or IP address \'\'\'$1\'\'\' has been blocked.',
 21+ 'regexblock-block-success' => 'Block succedeed',
 22+ 'regexblock-currently-blocked' => 'Currently blocked addresses:',
 23+ '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',
 24+ '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',
 25+ 'regexblock-page-title' => 'Regular expression name block',
 26+ 'regexblockstats' => 'Regex block statistics',
 27+ 'regexblock-help' => 'Use the form below to block write access from a specific IP address or username.
2528 This should be done only to prevent vandalism, and in accordance with policy.
2629 \'\'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.
2730 You can also block full IP addresses, meaning that no one logging in from them will be able to edit pages.
2831 Note: partial IP addresses will be treated by usernames in determining blocking.
2932 If no reason is specified, a default generic reason will be used.\'\'',
30 - 'regexblock-page-title-1' => 'Block address using regular expressions',
31 - 'regexblock-unblock-success' => 'Unblock succeeded',
32 - 'regexblock-unblock-log' => 'User name or IP address \'\'\'$1\'\'\' has been unblocked.',
33 - 'regexblock-unblock-error' => 'Error unblocking $1.
 33+ 'regexblock-page-title-1' => 'Block address using regular expressions',
 34+ '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.
 35+If you believe this is in error, please $1' ,
 36+ 'regexblock-reason-name' => 'This username is prevented from editing due to vandalism or other disruption.
 37+If you believe this is in error, please $1',
 38+ 'regexblock-reason-regex' => 'This username is prevented from editing due to vandalism or other disruption by a user with a similar name.
 39+Please create an alternate user name or $1 about the problem',
 40+ 'regexblock-form-username' => 'IP address or username:',
 41+ 'regexblock-form-reason' => 'Reason:',
 42+ 'regexblock-form-expiry' => 'Expiry:',
 43+ 'regexblock-form-match' => 'Exact match',
 44+ 'regexblock-form-account-block' => 'Block creation of new accounts',
 45+ 'regexblock-form-submit' => 'Block this user',
 46+ 'regexblock-form-submit-empty' => 'Give a user name or an IP address to block.',
 47+ 'regexblock-form-submit-regex' => 'Invalid regular expression.',
 48+ 'regexblock-form-submit-expiry' => 'Please specify an expiration period.',
 49+ 'regexblock-match-stats-record' => "$1 blocked '$2' on '$3' at '$4', logging from address '$5'",
 50+ 'regexblock-nodata-found' => 'No data found',
 51+ 'regexblock-stats-title' => 'Regex block statistics',
 52+ 'regexblock-unblock-success' => 'Unblock succeeded',
 53+ 'regexblock-unblock-log' => 'User name or IP address \'\'\'$1\'\'\' has been unblocked.',
 54+ 'regexblock-unblock-error' => 'Error unblocking $1.
3455 Probably there is no such user.',
35 - 'regexblock-form-username' => 'IP address or username:',
36 - 'regexblock-form-reason' => 'Reason:',
37 - 'regexblock-form-expiry' => 'Expiry:',
38 - 'regexblock-form-match' => 'Exact match',
39 - 'regexblock-form-account-block' => 'Block creation of new accounts',
40 - 'regexblock-form-submit' => 'Block this user',
41 - 'regexblock-block-log' => 'User name or IP address \'\'\'$1\'\'\' has been blocked.',
42 - 'regexblock-block-success' => 'Block succedeed',
43 - 'regexblock-form-submit-empty' => 'Give a user name or an IP address to block.',
44 - 'regexblock-form-submit-regex' => 'Invalid regular expression.',
45 - 'regexblock-form-submit-expiry' => 'Please specify an expiration period.',
46 - 'regexblock-already-blocked' => '$1 is already blocked.',
47 - 'regexblock-stats-title' => 'Regex block statistics',
48 - 'regexblock-stats-username' => 'For $1',
49 - 'regexblock-stats-times' => 'was blocked on',
50 - 'regexblock-stats-logging' => 'logging from address',
51 - 'regexblock-currently-blocked' => 'Currently blocked addresses:',
52 - 'regexblock-view-blocked' => 'View blocked by:',
53 - 'regexblock-view-all' => 'All',
54 - 'regexblock-view-go' => 'Go',
55 - 'regexblock-view-match' => '(exact match)',
56 - 'regexblock-view-regex' => '(regex match)',
57 - 'regexblock-view-account' => '(account creation block)',
58 - 'regexblock-view-reason' => 'reason: $1',
59 - 'regexblock-view-reason-default' => 'generic reason',
60 - 'regexblock-view-block-infinite' => 'permanent block',
 56+ 'regexblock-regex-filter' => ' or regex value: ',
 57+ 'regexblock-view-blocked' => 'View blocked by:',
 58+ 'regexblock-view-all' => 'All',
 59+ 'regexblock-view-go' => 'Go',
 60+ 'regexblock-view-match' => '(exact match)',
 61+ 'regexblock-view-regex' => '(regex match)',
 62+ 'regexblock-view-account' => '(account creation block)',
 63+ 'regexblock-view-reason' => 'reason: $1',
 64+ 'regexblock-view-reason-default' => 'generic reason',
 65+ 'regexblock-view-block-infinite' => 'permanent block',
6166 'regexblock-view-block-temporary' => 'expires on ',
62 - 'regexblock-view-block-expired' => 'EXPIRED on ',
63 - 'regexblock-view-block-by' => 'blocked by ',
64 - 'regexblock-view-block-unblock' => 'unblock',
65 - 'regexblock-view-stats' => '(stats)',
66 - 'regexblock-view-empty' => 'The list of blocked names and addresses is empty.',
67 - 'regexblock-view-time' => 'on $1',
 67+ 'regexblock-view-block-expired' => 'EXPIRED on ',
 68+ 'regexblock-view-block-by' => 'blocked by ',
 69+ 'regexblock-view-block-unblock' => 'unblock',
 70+ 'regexblock-view-stats' => 'stats',
 71+ 'regexblock-view-empty' => 'The list of blocked names and addresses is empty.',
 72+ 'regexblock-view-time' => 'on $1',
 73+ 'right-regexblock' => 'Block users from editing on all wikis on the wiki farm',
6874 );
6975
70 -/** Message documentation (Message documentation)
71 - * @author Jon Harald Søby
72 - * @author Purodha
73 - * @author SPQRobin
74 - */
75 -$messages['qqq'] = array(
76 - 'regexblock-desc' => 'Short description of this extension, shown in [[Special:Version]]. Do not translate or change links.',
77 - 'regexblock-special-desc' => 'Short description of this extension, shown in [[Special:Version]]. Do not translate or change links.',
78 - 'regexblock-stat-desc' => 'Short description of this extension, shown in [[Special:Version]]. Do not translate or change links.',
79 - 'regexblock-reason-ip' => 'Parameter $1 is <tt>$wgContactLink</tt>, which is by default "<tt><nowiki>[[Special:Contact|contact Wikia]]</nowiki></tt>".',
80 - 'regexblock-reason-name' => 'Parameter $1 is <tt>$wgContactLink</tt>, which is by default "<tt><nowiki>[[Special:Contact|contact Wikia]]</nowiki></tt>".',
81 - 'regexblock-reason-regex' => 'Parameter $1 is <tt>$wgContactLink</tt>, which is by default "<tt><nowiki>[[Special:Contact|contact Wikia]]</nowiki></tt>".',
82 - 'regexblock-form-reason' => '{{Identical|Reason}}',
83 - 'regexblock-form-expiry' => '{{Identical|Expiry}}',
84 - 'regexblock-form-match' => '{{Identical|Exact match}}',
85 - 'regexblock-already-blocked' => '{{Identical|$1 is already blocked}}',
86 - 'regexblock-stats-username' => '{{Identical|For $1}}',
87 - 'regexblock-view-all' => '{{Identical|All}}',
88 - 'regexblock-view-go' => '{{Identical|Go}}',
89 - 'regexblock-view-match' => '{{Identical|Exact match}}',
90 - 'regexblock-view-block-temporary' => '{{Identical|Expires on}}',
91 -);
92 -
9376 /** Niuean (ko e vagahau Niuē)
9477 * @author Jose77
9578 */
@@ -1970,5 +1953,4 @@
19711954 'regexblock-view-reason-default' => '一般原因',
19721955 'regexblock-view-block-infinite' => '永久封禁',
19731956 'regexblock-view-stats' => '(統計)',
1974 -);
1975 -
 1957+);
\ No newline at end of file
Index: trunk/extensions/regexBlock/regexBlock.php
@@ -1,11 +1,10 @@
22 <?php
3 -
4 -/**#@+
 3+/**
54 * 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
65 *
7 - * @addtogroup SpecialPage
8 - *
9 - * @author Bartek Łapiński
 6+ * @file
 7+ * @ingroup Extensions
 8+ * @author Bartek Łapiński <bartek at wikia-inc.com>
109 * @copyright Copyright © 2007, Wikia Inc.
1110 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
1211 */
@@ -14,63 +13,58 @@
1514 * Protect against register_globals vulnerabilities.
1615 * This line must be present before any global variable is referenced.
1716 */
18 -if (!defined('MEDIAWIKI')) die();
 17+if( !defined('MEDIAWIKI') )
 18+ die();
1919
20 -/* generic reasons */
 20+/* name of the block table */
 21+define('REGEXBLOCK_TABLE', 'blockedby');
 22+/* name of the statistic table */
 23+define('REGEXBLOCK_STATS_TABLE', 'stats_blockedby');
 24+define('REGEXBLOCK_MASK', '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/');
 25+/* modes for fetching data during blocking */
 26+define('REGEXBLOCK_MODE_NAMES', 0);
 27+define('REGEXBLOCK_MODE_IPS', 1);
 28+/* for future use */
 29+define('REGEXBLOCK_USE_STATS', 1);
 30+/* memcached expiration time (0 - infinite) */
 31+define('REGEXBLOCK_EXPIRE', 0);
 32+/* memcached keys */
 33+define('REGEXBLOCK_USER_KEY', 'regex_user_block');
 34+define('REGEXBLOCK_BLOCKERS_KEY', 'regex_blockers');
 35+define('REGEXBLOCK_SPECIAL_KEY', 'regexBlockSpecial');
 36+define('REGEXBLOCK_SPECIAL_NUM_RECORD', 'number_records');
2137
 38+/* add hook */
 39+$wgHooks['GetBlockedStatus'][] = 'wfRegexBlockCheck';
 40+
 41+/* generic reasons */
2242 global $wgContactLink;
2343
24 -if($wgContactLink == ''){
 44+if( $wgContactLink == '' ){
2545 $wgContactLink = '[[Special:Contact|contact us]]';
2646 }
2747
 48+// New user right
 49+$wgAvailableRights[] = 'regexblock';
 50+$wgGroupPermissions['staff']['regexblock'] = true;
 51+
 52+// Extension credits that will show up on Special:Version
2853 $wgExtensionCredits['specialpage'][] = array(
2954 'name' => 'regexBlock',
30 - 'author' => 'Bartek Łapiński',
 55+ 'author' => array( 'Bartek Łapiński', 'Tomasz Klim', 'Piotr Molski', 'Adrian Wieczorek' ),
3156 'url' => 'http://www.mediawiki.org/wiki/Extension:RegexBlock',
32 - 'svn-date' => '$LastChangedDate$',
33 - 'svn-revision' => '$LastChangedRevision$',
 57+ 'version' => '1.2',
3458 '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.',
3559 'descriptionmsg' => 'regexblock-desc',
3660 );
3761
38 -$dir = dirname(__FILE__) . '/';
 62+// Set up the new special page
 63+$dir = dirname( __FILE__ ) . '/';
3964 $wgExtensionMessagesFiles['RegexBlock'] = $dir . 'regexBlock.i18n.php';
 65+$wgAutoloadClasses['RegexBlockForm'] = $dir . 'SpecialRegexBlock.php';
 66+$wgSpecialPages['RegexBlock'] = 'RegexBlockForm';
 67+// Special page group for MW 1.13+
 68+$wgSpecialPageGroups['RegexBlock'] = 'users';
4069
41 -define ('REGEXBLOCK_PATH', '/') ;
42 -
43 -/* get name of the table */
44 -function wfRegexBlockGetTable () {
45 - global $wgSharedDB;
46 - if ("" != $wgSharedDB) {
47 - return "{$wgSharedDB}.blockedby";
48 - } else {
49 - return 'blockedby';
50 - }
51 -}
52 -
53 -/* get the name of the stats table */
54 -function wfRegexBlockGetStatsTable () {
55 - global $wgSharedDB;
56 - if ("" != $wgSharedDB) {
57 - return "{$wgSharedDB}.stats_blockedby";
58 - } else {
59 - return 'stats_blockedby';
60 - }
61 -}
62 -
63 -/* memcached expiration time (0 - infinite) */
64 -define ('REGEXBLOCK_EXPIRE', 0);
65 -/* modes for fetching data during blocking */
66 -define ('REGEXBLOCK_MODE_NAMES', 0);
67 -define ('REGEXBLOCK_MODE_IPS', 1);
68 -/* for future use */
69 -define ('REGEXBLOCK_USE_STATS', 1);
70 -
7170 /* core includes */
72 -require_once ($IP.REGEXBLOCK_PATH."extensions/regexBlock/regexBlockCore.php");
73 -require_once ($IP.REGEXBLOCK_PATH."extensions/regexBlock/SpecialRegexBlock.php");
74 -require_once ($IP.REGEXBLOCK_PATH."extensions/regexBlock/SpecialRegexBlockStats.php");
75 -
76 -/* simplified regexes, this is shared with SpamRegex */
77 -require_once ($IP.REGEXBLOCK_PATH."extensions/SimplifiedRegex/SimplifiedRegex.php");
 71+require_once("$IP/extensions/regexBlock/regexBlockCore.php");
\ No newline at end of file
Property changes on: trunk/extensions/regexBlock/regexBlock.php
___________________________________________________________________
Deleted: svn:keywords
7872 - LastChangedDate LastChangedRevision
Index: trunk/extensions/regexBlock/SpecialRegexBlock.php
@@ -1,465 +1,739 @@
22 <?php
3 -
4 -/**#@+
 3+/**
54 * A special page with the interface for blocking, viewing and unblocking
65 * user names and IP addresses
76 *
8 - * @addtogroup SpecialPage
9 - *
10 - * @author Bartek Łapiński
 7+ * @file
 8+ * @ingroup Extensions
 9+ * @author Bartek Łapiński <bartek at wikia-inc.com>, Piotr Molski <moli at wikia-inc.com>
1110 * @copyright Copyright © 2007, Wikia Inc.
1211 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
1312 */
1413
15 -if(!defined('MEDIAWIKI'))
16 - die();
 14+/**
 15+ * Protect against register_globals vulnerabilities.
 16+ * This line must be present before any global variable is referenced.
 17+ */
 18+if( !defined('MEDIAWIKI') )
 19+ die();
1720
18 -$wgAvailableRights[] = 'regexblock';
19 -$wgGroupPermissions['staff']['regexblock'] = true;
 21+class RegexBlockForm extends SpecialPage {
 22+ var $mRegexUnblockedAddress;
 23+ var $numResults = 0;
 24+ var $numStatResults = 0;
 25+ var $mPosted, $mAction;
 26+ var $mFilter, $mRegexFilter;
 27+ var $mLimit;
 28+ var $mOffset;
 29+ var $mError, $mMsg;
2030
21 -$wgExtensionFunctions[] = 'wfRegexBlockSetup';
22 -$wgExtensionCredits['specialpage'][] = array(
23 - 'name' => 'Regular Expression Name Block',
24 - 'author' => 'Bartek Łapiński',
25 - 'url' => 'http://www.mediawiki.org/wiki/Extension:RegexBlock',
26 - 'description' => 'alternate user block (by given name, using regular expressions)',
27 - 'descriptionmsg' => 'regexblock-special-desc',
28 -);
 31+ /**
 32+ * Constructor
 33+ */
 34+ public function __construct() {
 35+ $this->mPosted = false;
 36+ $this->mAction = '';
 37+ $this->mFilter = $this->mRegexFilter = '';
 38+ $this->mError = $this->mMsg = '';
 39+ parent::__construct( 'RegexBlock'/*class*/, 'regexblock'/*restriction*/ );
 40+ wfLoadExtensionMessages('RegexBlock');
 41+ }
2942
30 -/* special page init */
31 -$wgSpecialPageGroups['Regexblock'] = 'users';
32 -function wfRegexBlockSetup() {
33 - global $IP;
34 - if (!wfSimplifiedRegexCheckSharedDB())
35 - return;
36 - require_once($IP. '/includes/SpecialPage.php');
37 - SpecialPage::addPage(new SpecialPage('Regexblock', 'regexblock', true, 'wfRegexBlockSpecial', false));
38 - wfLoadExtensionMessages( 'RegexBlock' );
39 -}
 43+ /**
 44+ * Show the special page
 45+ *
 46+ * @param $subpage Mixed: parameter passed to the page or null
 47+ */
 48+ public function execute( $subpage ) {
 49+ global $wgUser, $wgOut, $wgRequest;
4050
41 -/* wrapper for GET values */
42 -function wfGetListBits () {
43 - global $wgRequest;
44 - $pieces = array();
45 - list( $limit, $offset ) = $wgRequest->getLimitOffset();
46 - $pieces[] = 'limit=' . $limit;
47 - $pieces[] = 'offset=' . $offset;
48 - $pieces[] = 'filter=' . urlencode ($wgRequest->getVal ('filter') );
49 - $bits = implode( '&', $pieces );
50 - return $bits;
51 -}
 51+ # If the user doesn't have the required 'regexblock' permission, display an error
 52+ if ( !$wgUser->isAllowed( 'regexblock' ) ) {
 53+ $this->displayRestrictionError();
 54+ return;
 55+ }
5256
53 -/* draws one option for select */
54 -function wfRegexBlockMakeOption ($name, $value, $current) {
55 - global $wgOut;
56 - if ($value == $current) {
57 - $wgOut->addHTML ("<option selected=\"selected\" value=\"{$value}\">{$name}</option>");
58 - } else {
59 - $wgOut->addHTML ("<option value=\"{$value}\">{$name}</option>");
60 - }
61 -}
 57+ # Show a message if the database is in read-only mode
 58+ if ( wfReadOnly() ) {
 59+ $wgOut->readOnlyPage();
 60+ return;
 61+ }
6262
63 -/* the core */
64 -function wfRegexBlockSpecial( $par ) {
65 - global $wgOut, $wgUser, $wgRequest;
66 - $wgOut->setPageTitle(wfMsg('regexblock-page-title'));
67 - $rBS = new regexBlockForm($par);
68 - $rBL = new regexBlockList($par);
 63+ # If user is blocked, s/he doesn't need to access this page
 64+ if ( $wgUser->isBlocked() ) {
 65+ $wgOut->blockedPage();
 66+ return;
 67+ }
6968
70 - $action = $wgRequest->getVal( 'action' );
71 - if ( 'success_block' == $action ) {
72 - $rBS->showSuccess();
73 - $rBS->showForm('');
74 - } else if ( 'success_unblock' == $action ) {
75 - $rBL->showSuccess();
76 - $rBS->showForm('');
77 - } else if ( 'failure_unblock' == $action ) {
78 - $ip = $wgRequest->getVal('ip');
79 - $rBS->showForm (wfMsgHtml('regexblock-unblock-error', $ip));
80 - } else if ( $wgRequest->wasPosted() && 'submit' == $action &&
81 - $wgUser->matchEditToken( $wgRequest->getVal ('wpEditToken') ) ) {
82 - $rBS->doSubmit();
83 - } else if ('delete' == $action) {
84 - $rBL->deleteFromRegexBlockList();
85 - } else {
86 - $rBS->showForm('');
87 - }
88 - $rBL->showList('', $offset );
89 -}
 69+ // Initial output
 70+ $this->mTitle = Title::makeTitle( NS_SPECIAL, 'RegexBlock' );
 71+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 72+ $wgOut->setPageTitle( wfMsg('regexblock-page-title') );
 73+ $wgOut->setArticleRelated( false );
9074
91 -/* useful for cleaning the memcached keys */
92 -function wfRegexBlockUnsetKeys ($blocker, $username) {
93 - global $wgMemc, $wgSharedDB;
94 - $wgMemc->delete ("$wgSharedDB:regexBlockSpecial:numResults");
95 - $wgMemc->delete ( str_replace( " ", "_", "$wgSharedDB:regexBlockCore:".REGEXBLOCK_MODE_NAMES.":blocker:$blocker") );
96 - $wgMemc->delete ( str_replace( " ", "_", "$wgSharedDB:regexBlockCore:".REGEXBLOCK_MODE_IPS.":blocker:$blocker") );
97 - $wgMemc->delete ("$wgSharedDB:regexBlockCore:blockers");
98 - $wgMemc->delete ( str_replace( " ", "_", "$wgSharedDB:regexBlockCore:blocked:$username") );
99 -}
 75+ $this->mPosted = $wgRequest->wasPosted();
 76+ $this->mAction = $wgRequest->getVal( 'action' );
 77+ $this->mFilter = $wgRequest->getVal( 'filter' );
 78+ $this->mRegexFilter = $wgRequest->getVal( 'rfilter' );
 79+
 80+ list( $this->mLimit, $this->mOffset ) = $wgRequest->getLimitOffset();
10081
101 -/* the list of blocked names/addresses */
102 -class regexBlockList {
103 - var $mRegexUnblockedAddress;
104 - var $numResults = 0;
 82+ $this->mRegexBlockedAddress = $this->mRegexBlockedExact = $this->mRegexBlockedCreation = $this->mRegexBlockedExpire = $this->mRegexBlockedReason = '';
 83+ if ( $this->mAction == 'submit' ) {
 84+ $this->mRegexBlockedAddress = htmlspecialchars( $wgRequest->getVal( 'wpRegexBlockedAddress', $wgRequest->getVal( 'ip' ) ) );
 85+ $this->mRegexBlockedExact = $wgRequest->getInt( 'wpRegexBlockedExact' );
 86+ $this->mRegexBlockedCreation = $wgRequest->getInt( 'wpRegexBlockedCreation' );
 87+ $this->mRegexBlockedExpire = htmlspecialchars( $wgRequest->getVal( 'wpRegexBlockedExpire' ) );
 88+ $this->mRegexBlockedReason = htmlspecialchars( $wgRequest->getVal( 'wpRegexBlockedReason' ) );
 89+ }
10590
106 - /* constructor */
107 - function regexBlockList ( $par ) {
 91+ /* Actions */
 92+ switch( $this->mAction ) {
 93+ case 'success_block':
 94+ $wgOut->setSubTitle( wfMsg( 'regexblock-block-success' ) );
 95+ $this->mMsg = wfMsgWikiHtml( 'regexblock-block-log', array( htmlspecialchars( $wgRequest->getVal( 'ip' ) ) ) );
 96+ break;
 97+ case 'success_unblock':
 98+ $wgOut->setSubTitle( wfMsg( 'regexblock-unblock-success' ) );
 99+ $this->mMsg = wfMsgWikiHtml( 'regexblock-unblock-log', array( htmlspecialchars( $wgRequest->getVal( 'ip' ) ) ) );
 100+ break;
 101+ case 'failure_unblock':
 102+ $this->mError = wfMsgWikiHtml( 'regexblock-unblock-error', array( htmlspecialchars( $wgRequest->getVal( 'ip' ) ) ) );
 103+ break;
 104+ case 'stats':
 105+ $blckid = $wgRequest->getVal( 'blckid' );
 106+ $this->showStatsList($blckid);
 107+ break;
 108+ case 'submit':
 109+ if ( $wgRequest->wasPosted() && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
 110+ $this->mAction = $this->doSubmit();
 111+ }
 112+ break;
 113+ case 'delete':
 114+ $this->deleteFromRegexBlockList();
 115+ break;
 116+ }
 117+
 118+ if ( !in_array( $this->mAction, array( 'submit', 'stats' ) ) ) {
 119+ $this->showForm();
 120+ unset($this->mError);
 121+ unset($this->mMsg);
 122+ $this->showRegexList();
 123+ }
108124 }
109125
110 - /* output list */
111 - function showList ( $err ) {
112 - global $wgOut, $wgRequest, $wgMemc, $wgLang, $wgUser;
 126+ /**
 127+ * Show the form for blocking IPs / users
 128+ */
 129+ private function showForm() {
 130+ global $wgOut, $wgUser, $wgRequest;
 131+ wfProfileIn( __METHOD__ );
 132+
 133+ $token = htmlspecialchars( $wgUser->editToken() );
 134+ $titleObj = Title::makeTitle( NS_SPECIAL, 'RegexBlock' );
 135+ $action = $titleObj->escapeLocalURL( "action=submit" )."&".$this->makeListUrlParams();
 136+ $err = $this->mError;
 137+ $msg = $this->mMsg;
113138
114 - /* on error, display error */
115 - if ( "" != $err ) {
116 - $wgOut->addHTML ("<p class='error'>{$err}</p>\n");
 139+ $expiries = RegexBlockData::getExpireValues();
 140+ $regexBlockAddress = ( empty( $this->mRegexBlockedAddress ) && ( $wgRequest->getVal( 'ip' ) != null ) &&
 141+ ( $wgRequest->getVal( 'action' ) == null ) ) ? $wgRequest->getVal( 'ip' ) : $this->mRegexBlockedAddress;
 142+
 143+ $wgOut->addHTML('<div style="float:left; clear:both; margin-left: auto; margin-right:auto">');
 144+ if ( '' != $err ) {
 145+ $wgOut->setSubtitle( wfMsgHtml( 'formerror' ) );
 146+ $wgOut->addHTML('<h2 class="errorbox">'.$this->mError.'</h2>');
 147+ } elseif ( $msg != '' ) {
 148+ $wgOut->addHTML('<h2 class="successbox">'.$this->mMsg.'</h2>');
117149 }
118 - $titleObj = Title::makeTitle( NS_SPECIAL, 'Regexblock' );
119 - $action = $titleObj->escapeLocalURL("") ."?".wfGetListBits();
120 - $action_unblock = $titleObj->escapeLocalURL("action=delete") ."&".wfGetListBits();
121 - list( $limit, $offset ) = $wgRequest->getLimitOffset();
 150+ $wgOut->addHTML('</div><div style="clear:both; width:auto;">'.wfMsgExt('regexblock-help', 'parse').'</div>
 151+ <fieldset style="width:90%; margin:auto;" align="center"><legend>'.wfMsg('regexblock-form-submit').'</legend>
 152+ <form name="regexblock" method="post" action="'.$action.'">
 153+ <table border="0">
 154+ <tr>
 155+ <td align="right">'.wfMsg('regexblock-form-username').'</td>
 156+ <td align="left">
 157+ <input tabindex="1" name="wpRegexBlockedAddress" id="wpRegexBlockedAddress" size="40" value="'.$regexBlockAddress.'" style="border: 1px solid #2F6FAB;" />
 158+ </td>
 159+ </tr>
 160+ <tr>
 161+ <td align="right">'.ucfirst( wfMsg('regexblock-form-reason') ).'</td>
 162+ <td align="left">
 163+ <input tabindex="2" name="wpRegexBlockedReason" id="wpRegexBlockedReason" size="40" value="'.$this->mRegexBlockedReason.'" style="border: 1px solid #2F6FAB;" />
 164+ </td>
 165+ </tr>
 166+ <tr>
 167+ <td align="right">'.ucfirst( wfMsg('regexblock-form-expiry') ).'</td>
 168+ <td align="left">
 169+ <select name="wpRegexBlockedExpire" id="wpRegexBlockedExpire" tabindex="3" style="border: 1px solid #2F6FAB;">'."\n");
 170+ foreach( $expiries as $k => $v ) {
 171+ $selected = htmlspecialchars( ($k == $this->mRegexBlockedExpire) ) ? ' selected="selected"' : '';
 172+ $wgOut->addHTML('<option value="'.htmlspecialchars($v).'"'.$selected.'>'.htmlspecialchars($v).'</option>');
 173+ }
 174+ $wgOut->addHTML('</select>');
 175+ $checkExact = htmlspecialchars( ($this->mRegexBlockedExact) ) ? 'checked="checked"' : '';
 176+ $checkCreation = htmlspecialchars( ($this->mRegexBlockedCreation) ) ? 'checked="checked"' : '';
 177+ $wgOut->addHTML('</td></tr>
 178+ <tr>
 179+ <td align="right">&nbsp;</td>
 180+ <td align="left">
 181+ <input type="checkbox" tabindex="4" name="wpRegexBlockedExact" id="wpRegexBlockedExact" value="1" '.$checkExact.' />
 182+ <label for="wpRegexBlockedExact">'. ucfirst( wfMsg('regexblock-form-match') ) .'</label>
 183+ </td></tr>
 184+ <tr>
 185+ <td align="right">&nbsp;</td>
 186+ <td align="left">
 187+ <input type="checkbox" tabindex="5" name="wpRegexBlockedCreation" id="wpRegexBlockedCreation" value="1" '.$checkCreation.' />
 188+ <label for="wpRegexBlockedCreation">'.wfMsg('regexblock-form-account-block').'</label>
 189+ </td></tr>
 190+ <tr>
 191+ <td align="right">&nbsp;</td>
 192+ <td align="left">
 193+ <input tabindex="6" name="wpRegexBlockedSubmit" type="submit" value="'.wfMsg('regexblock-form-submit').'" style="color:#2F6FAB;" />
 194+ </td></tr></table>
 195+ <input type="hidden" name="wpEditToken" value="'.$token.'" />
 196+ </form>
 197+ </fieldset>
 198+ <br />');
 199+ wfProfileOut( __METHOD__ );
 200+ }
122201
123 - $wgOut->addWikiText ("<br />'''".wfMsg('regexblock-currently-blocked')."'''");
124 - $this->fetchNumResults();
125 - $this->showPrevNext($wgOut);
 202+ /**
 203+ * Show the list of regex blocks - current and expired, along with some controls (unblock, statistics, etc.)
 204+ */
 205+ private function showRegexList() {
 206+ global $wgOut, $wgRequest, $wgMemc, $wgLang, $wgUser, $wgContLang;
126207
127 - $wgOut->addHTML ("<form name=\"regexlist\" method=\"get\" action=\"{$action}\">");
 208+ wfProfileIn( __METHOD__ );
128209
129 - /* allow display by specific blockers only */
130 - $wgOut->addHTML (wfMsg('regexblock-view-blocked')." <select name=\"filter\"><option value=\"\">".wfMsg('regexblock-view-all')."</option>") ;
131 - $blockers = $this->fetchBlockers();
132 - $wgOut->addHTML("</select>&#160;<input type=\"submit\" value=".wfMsg('regexblock-view-go')."><br /><br />
133 - ");
134 - $current = $wgRequest->getVal('filter');
 210+ $titleObj = Title::makeTitle( NS_SPECIAL, 'RegexBlock' );
 211+ $action = $titleObj->escapeLocalURL("") ."?".$this->makeListUrlParams();
 212+ $action_unblock = $titleObj->escapeLocalURL("action=delete") ."&".$this->makeListUrlParams();
135213
136 - if ($blockers) {
137 - /* get data and play with data */
138 - $dbr = &wfGetDB (DB_SLAVE);
139 - $query = "SELECT * FROM ".wfRegexBlockGetTable();
140 - if ('' != $current) {
141 - $query .= " WHERE blckby_blocker = {$dbr->addQuotes($current)}";
142 - }
143 - /* righto, order by name */
144 - $query .= " order by blckby_timestamp DESC";
 214+ $regexData = new RegexBlockData();
 215+ $this->numResults = $regexData->fetchNbrResults();
 216+ $filter = 'filter=' . urlencode($this->mFilter) . '&rfilter=' . urlencode($this->mRegexFilter);
 217+ $pager = wfViewPrevNext( $this->mOffset, $this->mLimit, $wgContLang->specialpage( 'RegexBlock' ), $filter, ($this->numResults - $this->mOffset) <= $this->mLimit );
145218
146 - $query = $dbr->limitResult ($query, $limit, $offset);
147 - $res = $dbr->query($query);
148 - $wgOut->addHTML("<ul>");
 219+ /* allow display by specific blockers only */
 220+ $blockers = $regexData->fetchBlockers();
 221+ $blocker_list = array();
 222+ if ( !empty( $blockers ) ) {
 223+ $blocker_list = $regexData->getBlockersData($this->mFilter, $this->mRegexFilter, $this->mLimit, $this->mOffset);
 224+ }
149225
150 - /* output a single row*/
151 - while ( $row = $dbr->fetchObject( $res ) ) {
152 - $ublock_ip = urlencode ($row->blckby_name);
153 - $ublock_blocker = urlencode ($row->blckby_blocker);
 226+ /* make link to statistics */
 227+ $mSkin = $wgUser->getSkin();
 228+ $wgOut->addHTML('<br /><b>'.wfMsg('regexblock-currently-blocked').'</b>
 229+ <p>'.$pager.'</p>
 230+ <form name="regexlist" method="get" action="'.htmlspecialchars($action).'">
 231+ '.wfMsg('regexblock-view-blocked').'
 232+ <select name="filter">
 233+ <option value="">'.wfMsg('regexblock-view-all').'</option>');
154234
155 - $row->blckby_exact ? $exact_match = wfMsgHtml('regexblock-view-match') : $exact_match = wfMsgHtml('regexblock-view-regex');
156 - $row->blckby_create ? $create_block = wfMsgHtml('regexblock-view-account') : $create_block = '';
157 - $row->blckby_reason ? $reason = "<i>".wfMsg('regexblock-view-reason',$row->blckby_reason)."</i>" : $reason = "<i>".wfMsg('regexblock-view-reason-default')."</i>";
 235+ if ( is_array( $blockers ) ) {
 236+ foreach( $blockers as $id => $blocker ) {
 237+ $sel = htmlspecialchars( ( $this->mFilter == $blocker ) ) ? ' selected="selected"' : '';
 238+ $wgOut->addHTML('<option value="'.htmlspecialchars($blocker).'"'. $sel.'>'.htmlspecialchars($blocker).'</option>');
 239+ }
 240+ }
158241
159 - $time = $wgLang->timeanddate( wfTimestamp( TS_MW, $row->blckby_timestamp ), true );
160 -
161 - /* if this block already expired, show it */
162 - $expiry = $row->blckby_expire;
163 - if ( (wfTimestampNow () <= $expiry) || ('infinite' == $expiry) ) {
164 - $expiry == 'infinite' ? $expires = wfMsg('regexblock-view-block-infinite') : $expires = wfMsg('regexblock-view-block-temporary').$wgLang->timeanddate( wfTimestamp( TS_MW, $expiry ), true );
 242+ $wgOut->addHTML('</select>&nbsp;'.wfMsg('regexblock-regex-filter').'<input type="text" name="rfilter" id="regex_filter" value="'.$this->mRegexFilter.'" />
 243+ <input type="submit" value="'.wfMsg('regexblock-view-go').'">
 244+ </form>
 245+ <br /><br />');
 246+ if ( !empty( $blockers ) ) {
 247+ $wgOut->addHTML('<ul>');
 248+ $loop = 0;
 249+ $comma = " <b>&#183;</b> "; // the spaces here are intentional
 250+ foreach( $blocker_list as $id => $row ) {
 251+ $loop++;
 252+ $color_expire = "%s";
 253+ if ( 'infinite' == $row['expiry'] ) {
 254+ $row['expiry'] = wfMsg('regexblock-view-block-infinite');
165255 } else {
166 - $expires = "<span style=\"color: #ff0000\">".wfMsg('regexblock-view-block-expired')."{$wgLang->timeanddate( wfTimestamp( TS_MW, $expiry ), true )}</span>";
 256+ if ( wfTimestampNow() > $row['expiry'] ) {
 257+ $color_expire = "<span style=\"color:#DC143C\">%s</span>";
 258+ }
 259+ $row['expiry'] = sprintf($color_expire, $wgLang->timeanddate( wfTimestamp( TS_MW, $row['expiry'] ), true ));
167260 }
168 - $sk = $wgUser->getSkin();
169 - $stats_link = $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Regexblockstats' ), wfMsg('regexblock-view-stats'), 'target=' . urlencode($row->blckby_name));
170 - $wgOut->addHTML ("
171 - <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>
172 - ");
 261+
 262+ $exact_match = (($row['exact_match']) ? wfMsg('regexblock-view-match') : wfMsg('regexblock-view-regex'));
 263+ $create_block = ($row['create_block']) ? wfMsg('regexblock-view-account') : '';
 264+ $reason = '<i>'.$row['reason'].'</i>';
 265+ $stats_link = $mSkin->makeKnownLinkObj( $titleObj, wfMsg('regexblock-view-stats'), 'action=stats&blckid=' . urlencode($row['blckid']) . '&' . $urls);
 266+
 267+ $wgOut->addHTML('<li style="border-bottom:1px dashed #778899; padding-bottom:2px;font-size:11px">
 268+ <b><font style="color:#3B7F07; font-size:12px">'.$row['blckby_name'].'</font>'.$comma.$exact_match.$create_block.'</b>'.$comma.'
 269+ ('.wfMsg('regexblock-view-block-by').': <b>'.$row['blocker'].'</b>, '.$reason.') '.wfMsg('regexblock-view-time', $row['time']).$comma.'
 270+ (<a href="'.$action_unblock.'&ip='.$row['ublock_ip'].'&blocker='.$row['ublock_blocker'].'">'.wfMsg('regexblock-view-block-unblock').'</a>) '.$comma.$row['expiry'].$comma.' ('.$stats_link.')
 271+ </li>');
173272 }
 273+ $wgOut->addHTML('</ul><br /><br /><p>'.$pager.'</p>');
 274+ } else {
 275+ $wgOut->addWikiMsg('regexblock-view-empty');
 276+ }
174277
175 - $dbr->freeResult($res);
176 - $wgOut->addHTML("</ul></form>");
177 - } else { /* empty list */
178 - $wgOut->addHTML (wfMsg('regexblock-view-empty')."<br /><br />");
 278+ wfProfileOut( __METHOD__ );
 279+ }
 280+
 281+ private function makeListUrlParams( $no_limit = false ) {
 282+ global $wgRequest;
 283+ $pieces = array();
 284+ if ( !$no_limit ) {
 285+ $pieces[] = 'limit=' . $this->mLimit;
 286+ $pieces[] = 'offset=' . $this->mOffset;
179287 }
180 - $this->showPrevNext($wgOut);
 288+ $pieces[] = 'filter=' . urlencode( $wgRequest->getVal( 'filter' ) );
 289+ $pieces[] = 'rfilter=' . urlencode( $wgRequest->getVal( 'rfilter' ) );
 290+
 291+ return implode( '&', $pieces );
181292 }
182293
183 - /* a plain html link wrapper */
184 - function produceLink ($url, $link, $text) {
185 - return $html_link = ("<a href=\"$url$link\">$text</a>");
 294+ /* On submit */
 295+ private function doSubmit() {
 296+ global $wgOut, $wgUser, $wgMemc;
 297+
 298+ wfProfileIn( __METHOD__ );
 299+
 300+ /* empty name */
 301+ if ( strlen($this->mRegexBlockedAddress) == 0 ) {
 302+ $this->mError = wfMsg('regexblock-form-submit-empty');
 303+ wfProfileOut( __METHOD__ );
 304+ return false;
 305+ }
 306+
 307+ /* castrate regexes */
 308+ if ( RegexBlockData::isValidRegex($this->mRegexBlockedAddress) ) {
 309+ $this->mError = wfMsg('regexblock-form-submit-regex');
 310+ wfProfileOut( __METHOD__ );
 311+ return false;
 312+ }
 313+
 314+ /* check expiry */
 315+ if ( strlen($this->mRegexBlockedExpire) == 0 ) {
 316+ $this->mError = wfMsg('regexblock-form-submit-expiry');
 317+ wfProfileOut( __METHOD__ );
 318+ return false;
 319+ }
 320+
 321+ if ( $this->mRegexBlockedExpire != 'infinite' ) {
 322+ $expiry = strtotime( $this->mRegexBlockedExpire );
 323+ if ( $expiry < 0 || $expiry === false ) {
 324+ $this->mError = wfMsg( 'ipb_expiry_invalid' );
 325+ wfProfileOut( __METHOD__ );
 326+ return false;
 327+ }
 328+ $expiry = wfTimestamp( TS_MW, $expiry );
 329+ } else {
 330+ $expiry = $this->mRegexBlockedExpire;
 331+ }
 332+
 333+ $result = RegexBlockData::blockUser($this->mRegexBlockedAddress, $expiry, $this->mRegexBlockedExact, $this->mRegexBlockedCreation, $this->mRegexBlockedReason);
 334+ /* clear memcached */
 335+ $uname = $wgUser->getName();
 336+ wfRegexBlockUnsetKeys($this->mRegexBlockedAddress);
 337+
 338+ wfProfileOut( __METHOD__ );
 339+
 340+ /* redirect */
 341+ $titleObj = Title::makeTitle( NS_SPECIAL, 'RegexBlock' );
 342+ $wgOut->redirect( $titleObj->getFullURL( 'action=success_block&ip=' .urlencode( $this->mRegexBlockedAddress )."&".$this->makeListUrlParams() ) );
 343+
 344+ return;
186345 }
187346
188 - /* remove name or address from list - without confirmation */
189 - function deleteFromRegexBlockList () {
 347+ /**
 348+ * Remove name or address from list - without confirmation
 349+ */
 350+ private function deleteFromRegexBlockList() {
190351 global $wgOut, $wgRequest, $wgMemc, $wgUser;
191 - $ip = $wgRequest->getVal('ip');
192 - $blocker = $wgRequest->getVal('blocker');
193 - /* delete */
194 - $dbw =& wfGetDB( DB_MASTER );
195 - $query = "DELETE FROM ".wfRegexBlockGetTable()." WHERE blckby_name = ".$dbw->addQuotes($ip);
196 - $dbw->query($query);
197 - $titleObj = Title::makeTitle( NS_SPECIAL, 'Regexblock' );
198 - if ( $dbw->affectedRows() ) {
199 - /* success */
200 - wfRegexBlockUnsetKeys ($blocker, $ip);
201 - $wgOut->redirect( $titleObj->getFullURL( 'action=success_unblock&ip='.urlencode($ip).'&'.wfGetListBits() ) );
 352+
 353+ wfProfileIn( __METHOD__ );
 354+
 355+ $result = false;
 356+ $ip = $wgRequest->getVal( 'ip' );
 357+ $blocker = $wgRequest->getVal( 'blocker' );
 358+ $titleObj = Title::makeTitle( NS_SPECIAL, 'RegexBlock' );
 359+
 360+ if ( function_exists( 'wfRegexBlockClearExpired' ) ) {
 361+ $result = wfRegexBlockClearExpired( $ip, $blocker );
202362 } else {
203 - $wgOut->redirect( $titleObj->getFullURL( 'action=failure_unblock&ip='.urlencode($ip).'&'.wfGetListBits() ) );
 363+ /* delete */
 364+ $dbw = wfGetDB( DB_MASTER );
 365+
 366+ $dbw->delete( REGEXBLOCK_TABLE,
 367+ array("blckby_name = {$dbw->addQuotes($ip)}"),
 368+ __METHOD__
 369+ );
 370+
 371+ if ( $dbw->affectedRows() ) {
 372+ /* success, remember to delete cache key */
 373+ wfRegexBlockUnsetKeys( $ip );
 374+ $result = true;
 375+ }
204376 }
 377+
 378+ wfProfileOut( __METHOD__ );
 379+ if ( $result === true ) {
 380+ $wgOut->redirect( $titleObj->getFullURL( 'action=success_unblock&ip='.urlencode($ip).'&'.$this->makeListUrlParams() ) );
 381+ } else {
 382+ $wgOut->redirect( $titleObj->getFullURL( 'action=failure_unblock&ip='.urlencode($ip).'&'.$this->makeListUrlParams() ) );
 383+ }
 384+
 385+ return;
205386 }
206387
207 - /* fetch names of all blockers and write them into select's options */
208 - function fetchBlockers () {
209 - global $wgOut, $wgRequest, $wgMemc, $wgSharedDB;
210 - /* memcached */
211 - $key = "$wgSharedDB:regexBlockCore:blockers";
212 - $current = $wgRequest->getVal('filter');
213 - $cached = $wgMemc->get($key);
214 - $fetched = 0;
215 - if (!is_array($cached)) {
216 - /* get from database */
217 - $blockers_array = array();
218 - $dbr =& wfGetDB (DB_SLAVE);
219 - $query = "SELECT blckby_blocker FROM ".wfRegexBlockGetTable();
220 - $query .= " GROUP BY blckby_blocker";
221 - $res = $dbr->query($query);
222 - while ( $row = $dbr->fetchObject( $res ) ) {
223 - wfRegexBlockmakeOption ($row->blckby_blocker, $row->blckby_blocker, $current);
224 - array_push ($blockers_array, $row->blckby_blocker);
 388+ /**
 389+ * Display some statistics when a user clicks stats link (&action=stats)
 390+ *
 391+ * @param $blckid Int: ID number of the block
 392+ */
 393+ private function showStatsList( $blckid ) {
 394+ global $wgOut, $wgLang, $wgUser, $wgContLang;
 395+
 396+ wfProfileIn( __METHOD__ );
 397+
 398+ $titleObj = Title::makeTitle( NS_SPECIAL, 'RegexBlock' );
 399+ $action = $titleObj->escapeLocalURL("") ."?".$this->makeListUrlParams(true);
 400+ $skin = $wgUser->getSkin();
 401+
 402+ $regexData = new RegexBlockData();
 403+ $this->numStatResults = $regexData->fetchNbrStatResults($blckid);
 404+ $filter = 'action=stats&filter=' . urlencode($this->mFilter) . '&blckid=' . urlencode($blckid);
 405+ $pager = wfViewPrevNext($this->mOffset, $this->mLimit, $wgContLang->specialpage( 'RegexBlock' ), $filter, ($this->numStatResults - $this->mOffset) <= $this->mLimit );
 406+
 407+ /* allow display by specific blockers only */
 408+ $blockInfo = $regexData->getRegexBlockById($blckid);
 409+ $stats_list = array();
 410+ if ( !empty( $blockInfo ) && ( is_object( $blockInfo ) ) ) {
 411+ $stats_list = $regexData->getStatsData($blckid, $this->mLimit, $this->mOffset);
 412+ }
 413+
 414+ $blocker_link = $skin->makeKnownLinkObj( $titleObj, $blockInfo->blckby_blocker, 'filter=' . urlencode($blockInfo->blckby_blocker) );
 415+ $blockername_link = $skin->makeKnownLinkObj( $titleObj, $blockInfo->blckby_name, 'rfilter=' . urlencode($blockInfo->blckby_name) );
 416+
 417+ $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 />');
 418+ if ( !empty( $stats_list ) ) {
 419+ $wgOut->addHTML('<p>'.$pager.'</p><br /><ul>');
 420+ foreach( $stats_list as $id => $row ) {
 421+ $wgOut->addHTML('<li style="border-bottom:1px dashed #778899; padding-bottom:2px;font-size:11px">
 422+ '.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) ).'
 423+ </li>');
225424 }
226 - $fetched = $dbr->numRows($res);
227 - $dbr->freeResult($res);
228 - $wgMemc->set ($key, $blockers_array);
 425+ $wgOut->addHTML('</ul><br /><p>'.$pager.'</p>');
229426 } else {
230 - /* get from memcached */
231 - foreach ($cached as $blocker) {
232 - wfRegexBlockmakeOption ($blocker, $blocker, $current);
233 - $fetched++;
234 - }
 427+ $wgOut->addWikiMsg('regexblock-nodata-found');
235428 }
236 - return $fetched;
 429+
 430+ wfProfileOut( __METHOD__ );
237431 }
 432+}
238433
239 - /* fetch number of all rows */
240 - function fetchNumResults () {
 434+/**
 435+ * @class RegexBlockData
 436+ * helper classes & functions
 437+ * @author Bartek Łapiński
 438+ * @author Piotr Molski
 439+ */
 440+class RegexBlockData {
 441+ var $mNbrResults;
 442+
 443+ public function __construct() {
 444+ $this->mNbrResults = 0;
 445+ }
 446+
 447+ /**
 448+ * Fetch number of all rows
 449+ */
 450+ public function fetchNbrResults() {
241451 global $wgMemc, $wgSharedDB;
242452
 453+ wfProfileIn( __METHOD__ );
 454+
 455+ $this->mNbrResults = 0;
243456 /* we use memcached here */
244 - $key = "$wgSharedDB:regexBlockSpecial:numResults";
245 - $cached = $wgMemc->get($key);
246 - if (is_null ($cached)) {
247 - $dbr = &wfGetDB (DB_SLAVE);
248 - $query_count = "SELECT COUNT(*) as n FROM ".wfRegexBlockGetTable();
249 - $res_count = $dbr->query($query_count);
250 - $row_count = $dbr->fetchObject($res_count);
251 - $this->numResults = $row_count->n;
252 - $wgMemc->set ($key, $this->numResults, REGEXBLOCK_EXPIRE);
253 - $dbr->freeResult ($res_count);
 457+ $key = wfForeignMemcKey( $wgSharedDB, '', REGEXBLOCK_SPECIAL_KEY, REGEXBLOCK_SPECIAL_NUM_RECORD );
 458+ $cached = $wgMemc->get( $key );
 459+
 460+ if ( empty( $cached ) ) {
 461+ $dbr = wfGetDB( DB_MASTER );
 462+
 463+ $oRes = $dbr->select( REGEXBLOCK_TABLE,
 464+ array("COUNT(*) AS cnt"),
 465+ array("blckby_blocker <> ''"),
 466+ __METHOD__
 467+ );
 468+
 469+ if ( $oRow = $dbr->fetchObject( $oRes ) ) {
 470+ $this->mNbrResults = $oRow->cnt;
 471+ }
 472+ $dbr->freeResult($oRes);
 473+ $wgMemc->set($key, $this->mNbrResults, REGEXBLOCK_EXPIRE);
254474 } else {
255 - $this->numResults = $cached;
 475+ $this->mNbrResults = $cached;
256476 }
257 - }
258477
259 - /* on success */
260 - function showSuccess () {
261 - global $wgOut, $wgRequest;
262 - $wgOut->setPageTitle(wfMsg('regexblock-page-title-1'));
263 - $wgOut->setSubTitle(wfMsg('regexblock-unblock-success'));
264 - $wgOut->addWikiText(wfMsg('regexblock-unblock-log', $wgRequest->getVal('ip', $par)));
 478+ wfProfileOut( __METHOD__ );
 479+ return $this->mNbrResults;
265480 }
266481
267 - /* init for showprevnext */
268 - function showPrevNext( &$out ) {
269 - global $wgContLang, $wgRequest;
270 - list( $limit, $offset ) = $wgRequest->getLimitOffset();
271 - $filter = 'filter=' . urlencode ( $wgRequest->getVal ('filter') );
272 - $html = wfViewPrevNext(
273 - $offset,
274 - $limit,
275 - $wgContLang->specialpage( 'Regexblock' ),
276 - $filter,
277 - ($this->numResults - $offset) <= $limit
278 - );
279 - $out->addHTML( '<p>' . $html . '</p>' );
 482+ public function getNbrResults() {
 483+ return $this->mNbrResults;
280484 }
281 -}
282485
283 -/* the form for blocking names and addresses */
284 -class regexBlockForm {
285 - var $mRegexBlockedAddress, $mRegexBlockedExact, $mRegexBlockedCreation, $mRegexBlockedExpire;
 486+ /**
 487+ * Fetch names of all blockers and write them into select's options
 488+ *
 489+ * @return $blockers_array
 490+ */
 491+ public function fetchBlockers() {
 492+ $blockers_array = array();
 493+ wfProfileIn( __METHOD__ );
286494
287 - /* constructor */
288 - function regexBlockForm ( $par ) {
289 - global $wgRequest;
290 - $this->mRegexBlockedAddress = $wgRequest->getVal( 'wpRegexBlockedAddress', $wgRequest->getVal( 'ip', $par ) );
291 - $this->mRegexBlockedExact = $wgRequest->getInt('wpRegexBlockedExact');
292 - $this->mRegexBlockedCreation = $wgRequest->getInt('wpRegexBlockedCreation');
293 - $this->mRegexBlockedExpire = $wgRequest->getVal('wpRegexBlockedExpire');
294 - $this->mRegexBlockedReason = $wgRequest->getVal('wpRegexBlockedReason');
 495+ if ( function_exists( 'wfRegexBlockGetBlockers' ) ) {
 496+ $blockers_array = wfRegexBlockGetBlockers();
 497+ } else {
 498+ global $wgMemc, $wgSharedDB;
 499+ $key = wfForeignMemcKey( $wgSharedDB, '', REGEXBLOCK_BLOCKERS_KEY );
 500+ $cached = $wgMemc->get( $key );
 501+
 502+ if ( !is_array( $cached ) ) {
 503+ /* get from database */
 504+ $dbr = wfGetDB( DB_MASTER );
 505+ $oRes = $dbr->select( REGEXBLOCK_TABLE,
 506+ array("blckby_blocker"),
 507+ array("blckby_blocker <> ''"),
 508+ __METHOD__,
 509+ array("GROUP BY" => "blckby_blocker")
 510+ );
 511+ while( $oRow = $dbr->fetchObject($oRes) ) {
 512+ $blockers_array[] = $oRow->blckby_blocker;
 513+ }
 514+ $dbr->freeResult($oRes);
 515+ $wgMemc->set( $key, $blockers_array, REGEXBLOCK_EXPIRE );
 516+ } else {
 517+ /* get from cache */
 518+ $blockers_array = $cached;
 519+ }
 520+ }
 521+
 522+ wfProfileOut( __METHOD__ );
 523+ return $blockers_array;
295524 }
296525
297 - /* output */
298 - function showForm ( $err ) {
299 - global $wgOut, $wgUser, $wgRequest;
 526+ /**
 527+ *
 528+ * @param $current
 529+ * @param $username
 530+ * @param $limit
 531+ * @param $offset
 532+ * @return $blocker_list
 533+ */
 534+ public function getBlockersData( $current = '', $username = '', $limit, $offset ) {
 535+ global $wgSharedDB, $wgLang, $wgUser;
300536
301 - $token = htmlspecialchars( $wgUser->editToken() );
302 - $titleObj = Title::makeTitle( NS_SPECIAL, 'Regexblock' );
303 - $action = $titleObj->escapeLocalURL( "action=submit" )."&".wfGetListBits();
 537+ wfProfileIn( __METHOD__ );
304538
305 - if ( "" != $err ) {
306 - $wgOut->setSubtitle( wfMsgHtml( 'formerror' ) );
307 - $wgOut->addHTML( "<p class='error'>{$err}</p>\n" );
 539+ $blocker_list = array();
 540+ /* get data and play with data */
 541+ $dbr = wfGetDB( DB_MASTER );
 542+ $conds = array("blckby_blocker <> ''");
 543+
 544+ if ( !empty( $current ) ) {
 545+ $conds = array("blckby_blocker = {$dbr->addQuotes($current)}");
308546 }
309547
310 - $wgOut->addWikiText (wfMsg('regexblock-help'));
 548+ if ( !empty( $username ) ) {
 549+ $conds = array("blckby_name LIKE {$dbr->addQuotes('%'.$username.'%')}");
 550+ }
311551
312 - if ( 'submit' == $wgRequest->getVal( 'action' )) {
313 - $scRegexBlockedAddress = htmlspecialchars ($this->mRegexBlockedAddress);
314 - $scRegexBlockedExpire = htmlspecialchars ($this->mRegexBlockedExpire);
315 - $scRegexBlockedReason = htmlspecialchars ($this->mRegexBlockedReason);
316 - $this->mRegexBlockedExact ? $checked_ex = "checked=\"checked\"" : $checked_ex = "";
317 - $this->mRegexBlockedCreation ? $checked_cr = "checked=\"checked\"" : $checked_cr = "";
318 - } else {
319 - $scRegexBlockedAddress = '';
320 - $checked_ex = '';
321 - $checked_cr = '';
 552+ $oRes = $dbr->select( REGEXBLOCK_TABLE,
 553+ array("blckby_id, blckby_name, blckby_blocker, blckby_timestamp, blckby_expire, blckby_create, blckby_exact, blckby_reason"),
 554+ $conds,
 555+ __METHOD__,
 556+ array("LIMIT" => $limit, "OFFSET" => $offset, "ORDER BY" => "blckby_id desc")
 557+ );
 558+
 559+ while( $oRow = $dbr->fetchObject($oRes) ) {
 560+ $ublock_ip = urlencode($oRow->blckby_name);
 561+ $ublock_blocker = urlencode($oRow->blckby_blocker);
 562+ $reason = ($oRow->blckby_reason) ? wfMsg('regexblock-form-reason') . $oRow->blckby_reason : wfMsg('regexblock-view-reason-default');
 563+ $time = $wgLang->timeanddate( wfTimestamp( TS_MW, $oRow->blckby_timestamp ), true );
 564+
 565+ /* put data to array */
 566+ $blocker_list[] = array(
 567+ 'blckby_name' => $oRow->blckby_name,
 568+ 'exact_match' => $oRow->blckby_exact,
 569+ 'create_block' => $oRow->blckby_create,
 570+ 'blocker' => $oRow->blckby_blocker,
 571+ 'reason' => $reason,
 572+ 'time' => $time,
 573+ 'ublock_ip' => $ublock_ip,
 574+ 'ublock_blocker' => $ublock_blocker,
 575+ 'expiry' => $oRow->blckby_expire,
 576+ 'blckid' => $oRow->blckby_id
 577+ );
322578 }
323 - $blockadressform = wfMsg('regexblock-form-username');
324 - $blockadressreason = wfMsg('regexblock-form-reason');
325 - $blockadressexpiry = wfMsg('regexblock-form-expiry');
326 - $wgOut->addHTML("
327 -<form name=\"regexblock\" method=\"post\" action=\"{$action}\">
328 - <table border=\"0\">
329 - <tr>
330 - <td align=\"right\">{$blockadressform}</td>
331 - <td align=\"left\">
332 - <input tabindex=\"1\" name=\"wpRegexBlockedAddress\" size=\"40\" value=\"{$scRegexBlockedAddress}\" />
333 - </td>
334 - </tr>
335 - <tr>
336 - <td align=\"right\">{$blockadressreason}</td>
337 - <td align=\"left\">
338 - <input tabindex=\"2\" name=\"wpRegexBlockedReason\" size=\"40\" value=\"{$scRegexBlockedReason}\" />
339 - </td>
340 - </tr>
341 - <tr>
342 - <td align=\"right\">{$blockadressexpiry}</td>
343 - <td align=\"left\">
344 - <select name=\"wpRegexBlockedExpire\" tabindex=\"3\">");
345 - $expiries = array (
346 - '1 hour',
347 - '2 hours',
348 - '4 hours',
349 - '6 hours',
350 - '1 day',
351 - '3 days',
352 - '1 week',
353 - '2 weeks',
354 - '1 month',
355 - '3 months',
356 - '1 year',
357 - 'infinite',
358 - ) ;
359 - foreach ($expiries as $duration) {
360 - wfRegexBlockMakeOption ($duration, $duration, $scRegexBlockedExpire);
361 - }
362 - $exactmatch = wfMsg('regexblock-form-match');
363 - $blockaccountcreation = wfMsg('regexblock-form-account-block');
364 - $submitformblock = wfMsg('regexblock-form-submit');
365 - $wgOut->addHTML("</select>
366 - </td>
367 - </tr>
368 - <tr>
369 - <td align=\"right\">&#160;</td>
370 - <td align=\"left\">
371 - <input type=\"checkbox\" tabindex=\"4\" name=\"wpRegexBlockedExact\" id=\"wpRegexBlockedExact\" value=\"1\" $checked_ex />
372 - <label for=\"wpRegexBlockedExact\">{$exactmatch}</label>
373 - </td>
374 - </tr>
375 - <tr>
376 - <td align=\"right\">&#160;</td>
377 - <td align=\"left\">
378 - <input type=\"checkbox\" tabindex=\"5\" name=\"wpRegexBlockedCreation\" id=\"wpRegexBlockedCreation\" value=\"1\" $checked_cr />
379 - <label for=\"wpRegexBlockedCreation\">{$blockaccountcreation}</label>
380 - </td>
381 - </tr>
382 - <tr>
383 - <td align=\"right\">&#160;</td>
384 - <td align=\"left\">
385 - <input tabindex=\"6\" name=\"wpRegexBlockedSubmit\" type=\"submit\" value={$submitformblock} />
386 - </td>
387 - </tr>
388 - </table>
389 - <input type='hidden' name='wpEditToken' value=\"{$token}\" />
390 -</form>");
 579+ $dbr->freeResult($oRes);
 580+
 581+ wfProfileOut( __METHOD__ );
 582+ return $blocker_list;
391583 }
392584
393 - /* on success */
394 - function showSuccess () {
395 - global $wgOut;
396 - $wgOut->setPageTitle (wfMsg('regexblock-page-title-1'));
397 - $wgOut->setSubTitle (wfMsg('regexblock-block-success'));
 585+ /**
 586+ * Fetch number of all stats rows
 587+ *
 588+ * @param $id Int: ID of the regexBlock entry (value of stats_blckby_id column in the REGEXBLOCK_STATS_TABLE database table)
 589+ * @return $nbrStats
 590+ */
 591+ public function fetchNbrStatResults( $id ) {
 592+ global $wgSharedDB;
398593
399 - $wgOut->addWikiText (wfMsg('regexblock-block-log', $this->mRegexBlockedAddress));
400 - }
 594+ wfProfileIn( __METHOD__ );
 595+ $nbrStats = 0;
401596
402 - /* on submit */
403 - function doSubmit () {
404 - global $wgOut, $wgUser, $wgMemc;
 597+ $dbr = wfGetDB( DB_SLAVE );
 598+ $oRes = $dbr->select( REGEXBLOCK_STATS_TABLE,
 599+ array("COUNT(*) AS cnt"),
 600+ array("stats_blckby_id = '".intval($id)."'"),
 601+ __METHOD__
 602+ );
405603
406 - /* empty name */
407 - if ( strlen($this->mRegexBlockedAddress) == 0 ) {
408 - $this->showForm (wfMsg('regexblock-form-submit-empty'));
409 - return;
 604+ if ( $oRow = $dbr->fetchObject($oRes) ) {
 605+ $nbrStats = $oRow->cnt;
410606 }
 607+ $dbr->freeResult($oRes);
411608
412 - /* castrate regexes */
413 - if (!$simple_regex = wfValidRegex ($this->mRegexBlockedAddress) ) {
414 - /* now, very generic comment - should the conditions change, this should too */
415 - $this->showForm (wfMsg('regexblock-form-submit-regex'));
416 - return;
417 - }
 609+ wfProfileOut( __METHOD__ );
 610+ return $nbrStats;
 611+ }
418612
419 - /* check expiry */
420 - if ( strlen ($this->mRegexBlockedExpire) == 0 ) {
421 - $this->showForm (wfMsg('regexblock-form-submit-expiry'));
422 - return;
 613+ /**
 614+ * Fetch all logs
 615+ *
 616+ * @param $id Int: ID of the regexBlock entry (value of stats_blckby_id column in the REGEXBLOCK_STATS_TABLE database table)
 617+ * @param $limit
 618+ * @param $offset
 619+ * @return $stats
 620+ */
 621+ public function getStatsData( $id, $limit = 50, $offset = 0 ) {
 622+ global $wgSharedDB;
 623+
 624+ wfProfileIn( __METHOD__ );
 625+ $stats = array();
 626+
 627+ /* from database */
 628+ $dbr = wfGetDB( DB_SLAVE );
 629+ $conds = array("stats_blckby_id = '".intval($id)."'");
 630+ $oRes = $dbr->select( REGEXBLOCK_STATS_TABLE,
 631+ array("stats_blckby_id", "stats_user", "stats_blocker", "stats_timestamp", "stats_ip", "stats_match", "stats_dbname"),
 632+ $conds,
 633+ __METHOD__,
 634+ array("LIMIT" => $limit, "OFFSET" => $offset, "ORDER BY" => "stats_timestamp DESC")
 635+ );
 636+
 637+ while( $oRow = $dbr->fetchObject($oRes) ) {
 638+ $stats[] = $oRow;
423639 }
 640+ $dbr->freeResult($oRes);
424641
425 - /* TODO - check infinite */
426 - if ($this->mRegexBlockedExpire != 'infinite') {
427 - $expiry = strtotime( $this->mRegexBlockedExpire );
428 - if ( $expiry < 0 || $expiry === false ) {
429 - $this->showForm( wfMsg( 'ipb_expiry_invalid' ) );
430 - return;
 642+ wfProfileOut( __METHOD__ );
 643+ return $stats;
 644+ }
 645+
 646+ /**
 647+ * Fetch record for selected identifier of regex block
 648+ *
 649+ * @param $id Int: ID of the regexBlock entry (value of blckby_id column in the REGEXBLOCK_STATS_TABLE database table)
 650+ * @return $record
 651+ */
 652+ public function getRegexBlockById( $id ) {
 653+ global $wgSharedDB;
 654+
 655+ wfProfileIn( __METHOD__ );
 656+ $record = null;
 657+
 658+ $dbr = wfGetDB( DB_MASTER );
 659+ $oRes = $dbr->select( REGEXBLOCK_TABLE,
 660+ array("blckby_id", "blckby_name", "blckby_blocker", "blckby_timestamp", "blckby_expire", "blckby_create", "blckby_exact", "blckby_reason"),
 661+ array("blckby_id = '".intval($id)."'"),
 662+ __METHOD__
 663+ );
 664+
 665+ if( $oRow = $dbr->fetchObject($oRes) ) {
 666+ $record = $oRow;
431667 }
432 - $expiry = wfTimestamp( TS_MW, $expiry );
433 - } else {
434 - $expiry = $this->mRegexBlockedExpire;
435 - }
 668+ $dbr->freeResult($oRes);
 669+
 670+ wfProfileOut( __METHOD__ );
 671+ return $record;
 672+ }
436673
 674+ /**
 675+ * Insert a block record to the REGEXBLOCK_TABLE database table
 676+ *
 677+ * @param $address
 678+ * @param $expiry Mixed: expiry time of the block
 679+ * @param $exact
 680+ * @param $creation
 681+ * @param $reason Mixed: given block reason, which will be displayed to the regexblocked user
 682+ */
 683+ static public function blockUser( $address, $expiry, $exact, $creation, $reason ) {
 684+ global $wgUser;
 685+
 686+ wfProfileIn( __METHOD__ );
437687 /* make insert */
438 - $dbw =& wfGetDB( DB_MASTER );
 688+ $dbw = wfGetDB( DB_MASTER );
439689 $name = $wgUser->getName();
440 - $timestamp = wfTimestampNow();
441690
442 - $query = "INSERT IGNORE INTO ".wfRegexBlockGetTable()."
443 - (blckby_id, blckby_name, blckby_blocker, blckby_timestamp, blckby_expire, blckby_exact, blckby_create, blckby_reason)
444 - VALUES (null,
445 - {$dbw->addQuotes($this->mRegexBlockedAddress)},
446 - {$dbw->addQuotes($name)},
447 - '{$timestamp}',
448 - '{$expiry}',
449 - {$this->mRegexBlockedExact},
450 - {$this->mRegexBlockedCreation},
451 - {$dbw->addQuotes($this->mRegexBlockedReason)}
452 - )";
453 - $dbw->query($query);
454 - /* duplicate entry */
455 - if (!$dbw->affectedRows()) {
456 - $this->showForm (wfMsg('regexblock-already-blocked', $this->mRegexBlockedAddress));
457 - return;
 691+ $oRes = $dbw->replace( REGEXBLOCK_TABLE,
 692+ array( 'blckby_id', 'blckby_name' ),
 693+ array(
 694+ 'blckby_id' => 'null',
 695+ 'blckby_name' => $address,
 696+ 'blckby_blocker' => $name,
 697+ 'blckby_timestamp' => wfTimestampNow(),
 698+ 'blckby_expire' => $expiry,
 699+ 'blckby_exact' => intval($exact),
 700+ 'blckby_create' => intval($creation),
 701+ 'blckby_reason' => $reason
 702+ ),
 703+ __METHOD__
 704+ );
 705+
 706+ wfProfileOut( __METHOD__ );
 707+ return true;
 708+ }
 709+
 710+ /**
 711+ * Gets and returns the expiry time values
 712+ *
 713+ * @return array Array of block expiry times
 714+ */
 715+ static public function getExpireValues() {
 716+ $expiry_values = explode( ",", wfMsg('regexblock-expire-duration') );
 717+ $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');
 718+
 719+ if ( !function_exists('array_combine') ) {
 720+ function array_combine($a, $b) {
 721+ $out = array();
 722+ foreach( $a as $k => $v ) {
 723+ $out[$v] = $b[$k];
 724+ }
 725+ return $out;
 726+ }
458727 }
459728
460 - wfRegexBlockUnsetKeys ($name, $this->mRegexBlockedAddress);
 729+ return array_combine($expiry_text, $expiry_values);
 730+ }
461731
462 - /* redirect */
463 - $titleObj = Title::makeTitle( NS_SPECIAL, 'Regexblock' ) ;
464 - $wgOut->redirect( $titleObj->getFullURL( 'action=success_block&ip=' .urlencode( $this->mRegexBlockedAddress )."&".wfGetListBits() ) );
465 - }
 732+ /**
 733+ * Check that the given regex is valid
 734+ *
 735+ * @param $text Mixed: regular expression to be tested for validity
 736+ */
 737+ static function isValidRegex( $text ) {
 738+ return ( sprintf( "%s", @preg_match("/{$text}/", 'regex') ) === '' );
 739+ }
466740 }
\ No newline at end of file
Index: trunk/extensions/regexBlock/README
@@ -1,49 +1,16 @@
 2+This is the readme file for regexBlock extension.
23
3 -# INSTALLATION
 4+==Authors==
 5+regexBlock was written by Bartek Łapiński, Tomasz Klim, Piotr Molski and Adrian Wieczorek for Wikia, Inc.
46
5 -# Note: this extension works best when used along with setting shared database and memcached
 7+==License==
 8+regexBlock is licensed under GNU General Public License 2.0 or later.
 9+See http://www.gnu.org/copyleft/gpl.html for more details.
610
7 -# 1. Copy the /regexBlock folder and regexBlock.php into the /extensions folder.
8 -# 2. Add require_once ("/extensions/regexBlock.php") ;
9 -# to your GlobalSettings.php file.
10 -# 3. Create required tables.
11 -# 4. Set $wgSharedDB to the name of a shared database of your choice (in GlobalSettings.php).
12 -# 5. Set $wgMainCacheType to 'CACHE_MEMCACHED' and $wgMemCachedServers. The latter could look like this:
13 -# $wgMemCachedServers = array ("127.0.0.1:11000"); (for a memcached server running on IP 127.0.0.1 and
14 -# port 11000).
 11+==Installing regexBlock==
 12+Installation instructions and more details can be found on the extension's infopage:
 13+http://www.mediawiki.org/wiki/Extension:RegexBlock
1514
16 -# Depending on your previous configuration, you may need to remove or comment a line including the older regexBlock.php
17 -# (probably in your GlobalSettings.php file).
18 -
19 -
20 -# Required tables
21 -# total: 2 tables
22 -
23 - CREATE TABLE `blockedby` (
24 - `blckby_id` int(5) NOT NULL auto_increment,
25 - `blckby_name` varchar(255) NOT NULL,
26 - `blckby_blocker` varchar(255) NOT NULL,
27 - `blckby_timestamp` char(14) NOT NULL,
28 - `blckby_expire` char(14) NOT NULL,
29 - `blckby_create` tinyint(1) NOT NULL default '1',
30 - `blckby_exact` tinyint(1) NOT NULL default '0',
31 - `blckby_reason` tinyblob NOT NULL,
32 - PRIMARY KEY (`blckby_id`),
33 - UNIQUE KEY `blckby_name` (`blckby_name`),
34 - KEY `blckby_timestamp` (`blckby_timestamp`),
35 - KEY `blckby_expire` (`blckby_expire`)
36 - ) ;
37 -
38 - CREATE TABLE `stats_blockedby` (
39 - `stats_id` int(8) NOT NULL auto_increment,
40 - `stats_user` varchar(255) NOT NULL,
41 - `stats_blocker` varchar(255) NOT NULL,
42 - `stats_timestamp` char(14) NOT NULL,
43 - `stats_ip` char(15) NOT NULL,
44 - PRIMARY KEY (`stats_id`),
45 - KEY `stats_timestamp` (`stats_timestamp`)
46 - ) ;
47 -
48 -
49 -# More details can be found in the included RegexBlock.xml (mediawiki XML
50 -# dump format) or RegexBlock.html files
 15+==Bugs==
 16+Bugs and issues can be reported on the extension's talk page:
 17+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
@@ -0,0 +1,28 @@
 2+CREATE TABLE `blockedby` (
 3+ `blckby_id` int(5) NOT NULL AUTO_INCREMENT,
 4+ `blckby_name` varchar(255) NOT NULL,
 5+ `blckby_blocker` varchar(255) NOT NULL,
 6+ `blckby_timestamp` char(14) NOT NULL,
 7+ `blckby_expire` char(14) NOT NULL,
 8+ `blckby_create` tinyint(1) NOT NULL DEFAULT '1',
 9+ `blckby_exact` tinyint(1) NOT NULL DEFAULT '0',
 10+ `blckby_reason` tinyblob NOT NULL,
 11+ PRIMARY KEY (`blckby_id`),
 12+ UNIQUE KEY `blckby_name` (`blckby_name`),
 13+ KEY `blckby_timestamp` (`blckby_timestamp`),
 14+ KEY `blckby_expire` (`blckby_expire`)
 15+) ENGINE=InnoDB;
 16+
 17+CREATE TABLE `stats_blockedby` (
 18+ `stats_id` int(8) NOT NULL AUTO_INCREMENT,
 19+ `stats_blckby_id` int(8) NOT NULL,
 20+ `stats_user` varchar(255) NOT NULL,
 21+ `stats_blocker` varchar(255) NOT NULL,
 22+ `stats_timestamp` char(14) NOT NULL,
 23+ `stats_ip` char(15) NOT NULL,
 24+ `stats_match` varchar(255) NOT NULL default '',
 25+ `stats_dbname` varchar(255) NOT NULL default '',
 26+ PRIMARY KEY (`stats_id`),
 27+ KEY `stats_blckby_id_key` (`stats_blckby_id`),
 28+ KEY `stats_timestamp` (`stats_timestamp`)
 29+) ENGINE=InnoDB;
\ No newline at end of file
Property changes on: trunk/extensions/regexBlock/schema.sql
___________________________________________________________________
Added: svn:eol-style
130 + native

Status & tagging log