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">:<small>See |
44 | | -[[Cross wiki user blocks]] for a list of users blocked the old |
45 | | -way.</small> |
46 | | -<H1>Product Requirements</H1> |
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 | | -* <strike>The blocks are permanent at the moment. So there is |
112 | | -more attention required to them.</strike> |
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&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 | | - ) ; |
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 | | - ) ; |
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 :) -- 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 :) -- 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 |
1 | 6 | + native |
Index: trunk/extensions/regexBlock/regexBlockCore.php |
— | — | @@ -1,234 +1,605 @@ |
2 | 2 | <?php |
3 | | -/**#@+ |
| 3 | +/** |
4 | 4 | * 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 |
5 | 5 | * |
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> |
9 | 11 | * @copyright Copyright © 2007, Wikia Inc. |
10 | 12 | * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later |
11 | 13 | */ |
12 | 14 | |
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__ ); |
16 | 21 | |
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__ ); |
24 | 28 | 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 ) ) { |
29 | 56 | /* 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; |
37 | 66 | } |
38 | | - $dbr->freeResult($res); |
39 | | - $wgMemc->set($key, $blockers_array, REGEXBLOCK_EXPIRE); |
| 67 | + $dbr->freeResult($oRes); |
| 68 | + $oMemc->set( $key, $blockers_array, REGEXBLOCK_EXPIRE ); |
40 | 69 | } else { |
41 | 70 | /* get from cache */ |
42 | | - foreach ($cached as $blocker) { |
43 | | - wfGetRegexBlocked ($blocker, $current_user, $ip_to_check); |
44 | | - } |
| 71 | + $blockers_array = $cached; |
45 | 72 | } |
46 | | - return true; |
| 73 | + |
| 74 | + wfProfileOut( __METHOD__ ); |
| 75 | + return $blockers_array; |
47 | 76 | } |
48 | 77 | |
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 ); |
60 | 88 | |
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); |
78 | 113 | } |
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'; |
85 | 222 | } |
| 223 | + $names[$key][] = $oRow->blckby_name; |
| 224 | + $loop++; |
86 | 225 | } |
| 226 | + $dbr->freeResult($oRes); |
| 227 | + |
| 228 | + if ( $loop > 0 ) { |
| 229 | + $blockData[$blocker] = $names; |
| 230 | + } |
87 | 231 | } |
88 | | - $wgMemc->set($key, $names, REGEXBLOCK_EXPIRE); |
89 | | - $dbr->freeResult($res); |
| 232 | + $oMemc->set( $memkey, $blockData, REGEXBLOCK_EXPIRE ); |
90 | 233 | } 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; |
95 | 240 | } |
96 | 241 | |
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; |
113 | 258 | } |
| 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 | + |
114 | 297 | $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 ) ) ) { |
124 | 309 | /* 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 ) ) { |
131 | 321 | /* 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; |
142 | 323 | } |
143 | | - $dbr->freeResult($res); |
| 324 | + $dbr->freeResult ($oRes); |
144 | 325 | } else { |
145 | 326 | /* 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__ ); |
150 | 338 | 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); |
153 | 342 | } |
154 | 343 | } |
155 | 344 | } |
| 345 | + |
| 346 | + wfProfileOut( __METHOD__ ); |
156 | 347 | return false; |
157 | 348 | } |
158 | 349 | |
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 | +} |
162 | 375 | |
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 | + |
168 | 393 | if ( $dbw->affectedRows() ) { |
169 | 394 | /* success, remember to delete cache key */ |
170 | | - wfRegexBlockUnsetKeys ($blocker, $username); |
171 | | - return true; |
| 395 | + wfRegexBlockUnsetKeys( $username ); |
| 396 | + $result = true; |
172 | 397 | } |
173 | | - return false; |
| 398 | + |
| 399 | + wfProfileOut( __METHOD__ ); |
| 400 | + return $result; |
174 | 401 | } |
175 | 402 | |
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; |
191 | 435 | } |
192 | | - return false; |
| 436 | + |
| 437 | + wfProfileOut( __METHOD__ ); |
| 438 | + return $result; |
193 | 439 | } |
194 | 440 | |
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__ ); |
208 | 451 | |
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; |
210 | 499 | /* 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 | + } |
215 | 505 | } |
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 ); |
228 | 557 | } |
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 | + } |
231 | 572 | |
232 | | - wfRegexBlockUpdateStats ($username, $user_ip, $blocker); |
233 | | - } |
| 573 | + $result = wfRegexBlockUpdateStats( $user, $user_ip, $blocker, $valid['match'], $valid['blckid'] ); |
234 | 574 | } |
| 575 | + |
| 576 | + wfProfileOut( __METHOD__ ); |
| 577 | + return $result; |
235 | 578 | } |
| 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 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | | - * Internationalisation file for extension regexBlock. |
| 4 | + * Internationalisation file for regexBlock extension. |
5 | 5 | * |
6 | | - * @addtogroup Extensions |
7 | | -*/ |
| 6 | + * @file |
| 7 | + * @ingroup Extensions |
| 8 | + */ |
8 | 9 | |
9 | 10 | $messages = array(); |
10 | 11 | |
| 12 | +/** English |
| 13 | + * @author Bartek Łapiński |
| 14 | + * @author Piotr Molski |
| 15 | + * @author Tomasz Klim |
| 16 | + */ |
11 | 17 | $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. |
25 | 28 | This should be done only to prevent vandalism, and in accordance with policy. |
26 | 29 | \'\'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. |
27 | 30 | You can also block full IP addresses, meaning that no one logging in from them will be able to edit pages. |
28 | 31 | Note: partial IP addresses will be treated by usernames in determining blocking. |
29 | 32 | 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. |
34 | 55 | 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', |
61 | 66 | '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', |
68 | 74 | ); |
69 | 75 | |
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 | | - |
93 | 76 | /** Niuean (ko e vagahau Niuē) |
94 | 77 | * @author Jose77 |
95 | 78 | */ |
— | — | @@ -1970,5 +1953,4 @@ |
1971 | 1954 | 'regexblock-view-reason-default' => '一般原因', |
1972 | 1955 | 'regexblock-view-block-infinite' => '永久封禁', |
1973 | 1956 | 'regexblock-view-stats' => '(統計)', |
1974 | | -); |
1975 | | - |
| 1957 | +); |
\ No newline at end of file |
Index: trunk/extensions/regexBlock/regexBlock.php |
— | — | @@ -1,11 +1,10 @@ |
2 | 2 | <?php |
3 | | - |
4 | | -/**#@+ |
| 3 | +/** |
5 | 4 | * 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 | 5 | * |
7 | | - * @addtogroup SpecialPage |
8 | | - * |
9 | | - * @author Bartek Łapiński |
| 6 | + * @file |
| 7 | + * @ingroup Extensions |
| 8 | + * @author Bartek Łapiński <bartek at wikia-inc.com> |
10 | 9 | * @copyright Copyright © 2007, Wikia Inc. |
11 | 10 | * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later |
12 | 11 | */ |
— | — | @@ -14,63 +13,58 @@ |
15 | 14 | * Protect against register_globals vulnerabilities. |
16 | 15 | * This line must be present before any global variable is referenced. |
17 | 16 | */ |
18 | | -if (!defined('MEDIAWIKI')) die(); |
| 17 | +if( !defined('MEDIAWIKI') ) |
| 18 | + die(); |
19 | 19 | |
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'); |
21 | 37 | |
| 38 | +/* add hook */ |
| 39 | +$wgHooks['GetBlockedStatus'][] = 'wfRegexBlockCheck'; |
| 40 | + |
| 41 | +/* generic reasons */ |
22 | 42 | global $wgContactLink; |
23 | 43 | |
24 | | -if($wgContactLink == ''){ |
| 44 | +if( $wgContactLink == '' ){ |
25 | 45 | $wgContactLink = '[[Special:Contact|contact us]]'; |
26 | 46 | } |
27 | 47 | |
| 48 | +// New user right |
| 49 | +$wgAvailableRights[] = 'regexblock'; |
| 50 | +$wgGroupPermissions['staff']['regexblock'] = true; |
| 51 | + |
| 52 | +// Extension credits that will show up on Special:Version |
28 | 53 | $wgExtensionCredits['specialpage'][] = array( |
29 | 54 | 'name' => 'regexBlock', |
30 | | - 'author' => 'Bartek Łapiński', |
| 55 | + 'author' => array( 'Bartek Łapiński', 'Tomasz Klim', 'Piotr Molski', 'Adrian Wieczorek' ), |
31 | 56 | 'url' => 'http://www.mediawiki.org/wiki/Extension:RegexBlock', |
32 | | - 'svn-date' => '$LastChangedDate$', |
33 | | - 'svn-revision' => '$LastChangedRevision$', |
| 57 | + 'version' => '1.2', |
34 | 58 | '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.', |
35 | 59 | 'descriptionmsg' => 'regexblock-desc', |
36 | 60 | ); |
37 | 61 | |
38 | | -$dir = dirname(__FILE__) . '/'; |
| 62 | +// Set up the new special page |
| 63 | +$dir = dirname( __FILE__ ) . '/'; |
39 | 64 | $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'; |
40 | 69 | |
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 | | - |
71 | 70 | /* 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 |
78 | 72 | - LastChangedDate LastChangedRevision |
Index: trunk/extensions/regexBlock/SpecialRegexBlock.php |
— | — | @@ -1,465 +1,739 @@ |
2 | 2 | <?php |
3 | | - |
4 | | -/**#@+ |
| 3 | +/** |
5 | 4 | * A special page with the interface for blocking, viewing and unblocking |
6 | 5 | * user names and IP addresses |
7 | 6 | * |
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> |
11 | 10 | * @copyright Copyright © 2007, Wikia Inc. |
12 | 11 | * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later |
13 | 12 | */ |
14 | 13 | |
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(); |
17 | 20 | |
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; |
20 | 30 | |
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 | + } |
29 | 42 | |
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; |
40 | 50 | |
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 | + } |
52 | 56 | |
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 | + } |
62 | 62 | |
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 | + } |
69 | 68 | |
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 ); |
90 | 74 | |
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(); |
100 | 81 | |
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 | + } |
105 | 90 | |
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 | + } |
108 | 124 | } |
109 | 125 | |
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; |
113 | 138 | |
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>'); |
117 | 149 | } |
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"> </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"> </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"> </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 | + } |
122 | 201 | |
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; |
126 | 207 | |
127 | | - $wgOut->addHTML ("<form name=\"regexlist\" method=\"get\" action=\"{$action}\">"); |
| 208 | + wfProfileIn( __METHOD__ ); |
128 | 209 | |
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> <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(); |
135 | 213 | |
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 ); |
145 | 218 | |
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 | + } |
149 | 225 | |
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>'); |
154 | 234 | |
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 | + } |
158 | 241 | |
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> '.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>·</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'); |
165 | 255 | } 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 )); |
167 | 260 | } |
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>'); |
173 | 272 | } |
| 273 | + $wgOut->addHTML('</ul><br /><br /><p>'.$pager.'</p>'); |
| 274 | + } else { |
| 275 | + $wgOut->addWikiMsg('regexblock-view-empty'); |
| 276 | + } |
174 | 277 | |
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; |
179 | 287 | } |
180 | | - $this->showPrevNext($wgOut); |
| 288 | + $pieces[] = 'filter=' . urlencode( $wgRequest->getVal( 'filter' ) ); |
| 289 | + $pieces[] = 'rfilter=' . urlencode( $wgRequest->getVal( 'rfilter' ) ); |
| 290 | + |
| 291 | + return implode( '&', $pieces ); |
181 | 292 | } |
182 | 293 | |
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; |
186 | 345 | } |
187 | 346 | |
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() { |
190 | 351 | 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 ); |
202 | 362 | } 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 | + } |
204 | 376 | } |
| 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; |
205 | 386 | } |
206 | 387 | |
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>, <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>'); |
225 | 424 | } |
226 | | - $fetched = $dbr->numRows($res); |
227 | | - $dbr->freeResult($res); |
228 | | - $wgMemc->set ($key, $blockers_array); |
| 425 | + $wgOut->addHTML('</ul><br /><p>'.$pager.'</p>'); |
229 | 426 | } else { |
230 | | - /* get from memcached */ |
231 | | - foreach ($cached as $blocker) { |
232 | | - wfRegexBlockmakeOption ($blocker, $blocker, $current); |
233 | | - $fetched++; |
234 | | - } |
| 427 | + $wgOut->addWikiMsg('regexblock-nodata-found'); |
235 | 428 | } |
236 | | - return $fetched; |
| 429 | + |
| 430 | + wfProfileOut( __METHOD__ ); |
237 | 431 | } |
| 432 | +} |
238 | 433 | |
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() { |
241 | 451 | global $wgMemc, $wgSharedDB; |
242 | 452 | |
| 453 | + wfProfileIn( __METHOD__ ); |
| 454 | + |
| 455 | + $this->mNbrResults = 0; |
243 | 456 | /* 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); |
254 | 474 | } else { |
255 | | - $this->numResults = $cached; |
| 475 | + $this->mNbrResults = $cached; |
256 | 476 | } |
257 | | - } |
258 | 477 | |
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; |
265 | 480 | } |
266 | 481 | |
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; |
280 | 484 | } |
281 | | -} |
282 | 485 | |
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__ ); |
286 | 494 | |
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; |
295 | 524 | } |
296 | 525 | |
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; |
300 | 536 | |
301 | | - $token = htmlspecialchars( $wgUser->editToken() ); |
302 | | - $titleObj = Title::makeTitle( NS_SPECIAL, 'Regexblock' ); |
303 | | - $action = $titleObj->escapeLocalURL( "action=submit" )."&".wfGetListBits(); |
| 537 | + wfProfileIn( __METHOD__ ); |
304 | 538 | |
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)}"); |
308 | 546 | } |
309 | 547 | |
310 | | - $wgOut->addWikiText (wfMsg('regexblock-help')); |
| 548 | + if ( !empty( $username ) ) { |
| 549 | + $conds = array("blckby_name LIKE {$dbr->addQuotes('%'.$username.'%')}"); |
| 550 | + } |
311 | 551 | |
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 | + ); |
322 | 578 | } |
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\"> </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\"> </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\"> </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; |
391 | 583 | } |
392 | 584 | |
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; |
398 | 593 | |
399 | | - $wgOut->addWikiText (wfMsg('regexblock-block-log', $this->mRegexBlockedAddress)); |
400 | | - } |
| 594 | + wfProfileIn( __METHOD__ ); |
| 595 | + $nbrStats = 0; |
401 | 596 | |
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 | + ); |
405 | 603 | |
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; |
410 | 606 | } |
| 607 | + $dbr->freeResult($oRes); |
411 | 608 | |
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 | + } |
418 | 612 | |
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; |
423 | 639 | } |
| 640 | + $dbr->freeResult($oRes); |
424 | 641 | |
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; |
431 | 667 | } |
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 | + } |
436 | 673 | |
| 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__ ); |
437 | 687 | /* make insert */ |
438 | | - $dbw =& wfGetDB( DB_MASTER ); |
| 688 | + $dbw = wfGetDB( DB_MASTER ); |
439 | 689 | $name = $wgUser->getName(); |
440 | | - $timestamp = wfTimestampNow(); |
441 | 690 | |
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 | + } |
458 | 727 | } |
459 | 728 | |
460 | | - wfRegexBlockUnsetKeys ($name, $this->mRegexBlockedAddress); |
| 729 | + return array_combine($expiry_text, $expiry_values); |
| 730 | + } |
461 | 731 | |
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 | + } |
466 | 740 | } |
\ No newline at end of file |
Index: trunk/extensions/regexBlock/README |
— | — | @@ -1,49 +1,16 @@ |
| 2 | +This is the readme file for regexBlock extension. |
2 | 3 | |
3 | | -# INSTALLATION |
| 4 | +==Authors== |
| 5 | +regexBlock was written by Bartek Łapiński, Tomasz Klim, Piotr Molski and Adrian Wieczorek for Wikia, Inc. |
4 | 6 | |
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. |
6 | 10 | |
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 |
15 | 14 | |
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§ion=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 |
1 | 30 | + native |