Index: trunk/extensions/BotQuery/query.php |
— | — | @@ -1,25 +1,25 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | | - * Bot Query extension for MediaWiki 1.7+ |
5 | | - * |
6 | | - * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com> |
7 | | - * Uses bits from the original query.php code written by Tim Starling. |
8 | | - * |
9 | | - * This program is free software; you can redistribute it and/or modify |
10 | | - * it under the terms of the GNU General Public License as published by |
11 | | - * the Free Software Foundation; either version 2 of the License, or |
12 | | - * (at your option) any later version. |
13 | | - * |
14 | | - * This program is distributed in the hope that it will be useful, |
15 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | - * GNU General Public License for more details. |
18 | | - * |
19 | | - * You should have received a copy of the GNU General Public License along |
20 | | - * with this program; if not, write to the Free Software Foundation, Inc., |
21 | | - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
22 | | - * http://www.gnu.org/copyleft/gpl.html |
23 | | - */ |
| 4 | +* Bot Query extension for MediaWiki 1.7+ |
| 5 | +* |
| 6 | +* Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com> |
| 7 | +* Uses bits from the original query.php code written by Tim Starling. |
| 8 | +* |
| 9 | +* This program is free software; you can redistribute it and/or modify |
| 10 | +* it under the terms of the GNU General Public License as published by |
| 11 | +* the Free Software Foundation; either version 2 of the License, or |
| 12 | +* (at your option) any later version. |
| 13 | +* |
| 14 | +* This program is distributed in the hope that it will be useful, |
| 15 | +* but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | +* GNU General Public License for more details. |
| 18 | +* |
| 19 | +* You should have received a copy of the GNU General Public License along |
| 20 | +* with this program; if not, write to the Free Software Foundation, Inc., |
| 21 | +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 22 | +* http://www.gnu.org/copyleft/gpl.html |
| 23 | +*/ |
24 | 24 | |
25 | 25 | |
26 | 26 | define( 'MEDIAWIKI', true ); |
— | — | @@ -42,7 +42,8 @@ |
43 | 43 | define( 'GEN_MIME', 1 ); |
44 | 44 | define( 'GEN_ISMETA', 1 ); |
45 | 45 | define( 'GEN_PARAMS', 2 ); |
46 | | -define( 'GEN_DESC', 3 ); |
| 46 | +define( 'GEN_DEFAULTS', 3 ); |
| 47 | +define( 'GEN_DESC', 4 ); |
47 | 48 | |
48 | 49 | $db =& wfGetDB( DB_SLAVE ); |
49 | 50 | $bqp = new BotQueryProcessor( $db ); |
— | — | @@ -59,10 +60,14 @@ |
60 | 61 | * 0) Function to call |
61 | 62 | * 1) mime type |
62 | 63 | * 2) array of accepted parameters |
63 | | - * 3) Format description |
| 64 | + * 3) array of default parameter values |
| 65 | + * 4) Format description |
64 | 66 | */ |
65 | 67 | var $outputGenerators = array( |
66 | | - 'xml' => array( 'printXML', 'text/xml', array('xmlindent','nousage'), array( |
| 68 | + 'xml' => array( 'printXML', 'text/xml', |
| 69 | + array('xmlindent', 'nousage'), |
| 70 | + array(null, null), |
| 71 | + array( |
67 | 72 | "XML format", |
68 | 73 | "Optional indentation can be enabled by supplying 'xmlindent' parameter.", |
69 | 74 | "Errors will return this usage screen, unless 'nousage' parameter is given.", |
— | — | @@ -70,29 +75,32 @@ |
71 | 76 | "Please use other browsers or switch to html format while debuging.", |
72 | 77 | "Example: query.php?what=info&format=xml", |
73 | 78 | )), |
74 | | - 'html'=> array( 'printHTML', 'text/html', array('nousage'), array( |
| 79 | + 'html'=> array( 'printHTML', 'text/html', |
| 80 | + array('nousage'), |
| 81 | + array(null), |
| 82 | + array( |
75 | 83 | "HTML format", |
76 | 84 | "The data is presented as an indented syntax-highlighted XML format.", |
77 | 85 | "Errors will return this usage screen, unless 'nousage' parameter is given.", |
78 | 86 | "Example: query.php?what=info&format=html", |
79 | 87 | )), |
80 | | - 'txt' => array( 'printHumanReadable', 'application/x-wiki-botquery-print_r', null, array( |
| 88 | + 'txt' => array( 'printHumanReadable', 'application/x-wiki-botquery-print_r', null, null, array( |
81 | 89 | "Human-readable format using print_r() (http://www.php.net/print_r)", |
82 | 90 | "Example: query.php?what=info&format=txt", |
83 | 91 | )), |
84 | | - 'json'=> array( 'printJSON', 'application/json', null, array( |
| 92 | + 'json'=> array( 'printJSON', 'application/json', null, null, array( |
85 | 93 | "JSON format (http://en.wikipedia.org/wiki/JSON)", |
86 | 94 | "Example: query.php?what=info&format=json", |
87 | 95 | )), |
88 | | - 'php' => array( 'printPHP', 'application/vnd.php.serialized', null, array( |
| 96 | + 'php' => array( 'printPHP', 'application/vnd.php.serialized', null, null, array( |
89 | 97 | "PHP serialized format using serialize() (http://www.php.net/serialize)", |
90 | 98 | "Example: query.php?what=info&format=php", |
91 | 99 | )), |
92 | | - 'dbg' => array( 'printParsableCode', 'application/x-wiki-botquery-var_export', null, array( |
| 100 | + 'dbg' => array( 'printParsableCode', 'application/x-wiki-botquery-var_export', null, null, array( |
93 | 101 | "PHP source code format using var_export() (http://www.php.net/var_export)", |
94 | 102 | "Example: query.php?what=info&format=dbg", |
95 | 103 | )), |
96 | | -// 'tsv' => array( 'print', 'text/tab-separated-values', null, '' ), |
| 104 | +// 'tsv' => array( 'print', 'text/tab-separated-values', null, null, '' ), |
97 | 105 | ); |
98 | 106 | |
99 | 107 | /** |
— | — | @@ -100,53 +108,73 @@ |
101 | 109 | * 0) Function to call |
102 | 110 | * 1) true/false - does this property work on individual pages? (false for site's metadata) |
103 | 111 | * 2) array of accepted parameters |
104 | | - * 3) Format description |
| 112 | + * 3) array of default parameter values |
| 113 | + * 4) Format description |
105 | 114 | */ |
106 | 115 | var $propGenerators = array( |
107 | 116 | |
108 | 117 | // Site-wide Generators |
109 | | - 'info' => array( "genMetaSiteInfo", true, null, array( |
| 118 | + 'info' => array( "genMetaSiteInfo", true, null, null, array( |
110 | 119 | "General site information", |
111 | 120 | "Example: query.php?what=info", |
112 | 121 | )), |
113 | | - 'namespaces' => array( "genMetaNamespaceInfo", true, null, array( |
| 122 | + 'namespaces' => array( "genMetaNamespaceInfo", true, null, null, array( |
114 | 123 | "List of localized namespace names", |
115 | 124 | "Example: query.php?what=namespaces", |
116 | 125 | )), |
117 | | - 'userinfo' => array( "genMetaUserInfo", true, null, array( |
| 126 | + 'userinfo' => array( "genMetaUserInfo", true, null, null, array( |
118 | 127 | "Information about current user", |
119 | 128 | "Example: query.php?what=userinfo", |
120 | 129 | )), |
121 | | - 'recentchanges' => array( "genMetaRecentChanges", true, array( 'rcfrom','rclimit','rchide' ), array( |
| 130 | + 'recentchanges' => array( "genMetaRecentChanges", true, |
| 131 | + array( 'rcfrom', 'rclimit', 'rchide' ), |
| 132 | + array( null, 50, array(null, 'minor', 'bots', 'anons', 'liu') ), |
| 133 | + array( |
122 | 134 | "Adds recently changed articles to the output list.", |
123 | 135 | "Parameters supported:", |
124 | 136 | "rcfrom - Timestamp of the first entry to start from. The list order reverses.", |
125 | 137 | "rclimit - how many total links to return.", |
126 | 138 | " Smaller size is possible if pages changes multiple times.", |
127 | | - "rchide - Which entries to ignore 'minor','bots','anons','liu' (loged-in users).", |
| 139 | + "rchide - Which entries to ignore 'minor', 'bots', 'anons', 'liu' (loged-in users).", |
128 | 140 | " Cannot specify both anons and liu.", |
129 | 141 | "Example: query.php?what=recentchanges&rchide=liu|bots", |
130 | 142 | )), |
131 | | - 'dblredirects' => array( "genMetaDoubleRedirects", true, null, array( |
| 143 | + 'users' => array( "genUserPages", true, |
| 144 | + array( 'usfrom', 'uslimit' ), |
| 145 | + array( null, 50 ), |
| 146 | + array( |
| 147 | + "Adds user pages to the output list.", |
| 148 | + "Parameters supported:", |
| 149 | + "usfrom - Start user listing from...", |
| 150 | + "uslimit - how many total links to return.", |
| 151 | + "Example: query.php?what=users&usfrom=Y", |
| 152 | + )), |
| 153 | + 'dblredirects' => array( "genMetaDoubleRedirects", true, |
| 154 | + array('dfoffset', 'drlimit'), |
| 155 | + array(null, 50), |
| 156 | + array( |
132 | 157 | "List of double-redirect pages", |
133 | 158 | "THIS QUERY IS CURRENTLY DISABLED DUE TO PERFORMANCE REASONS", |
134 | 159 | "Example: query.php?what=dblredirects", |
135 | 160 | )), |
136 | 161 | |
137 | 162 | // Page-specific Generators |
138 | | - 'links' => array( "genPageLinks", false, null, array( |
| 163 | + 'links' => array( "genPageLinks", false, null, null, array( |
139 | 164 | "List of regular page links", |
140 | 165 | "Example: query.php?what=links&titles=MediaWiki|Wikipedia", |
141 | 166 | )), |
142 | | - 'langlinks' => array( "genPageLangLinks", false, null, array( |
| 167 | + 'langlinks' => array( "genPageLangLinks", false, null, null, array( |
143 | 168 | "Inter-language links", |
144 | 169 | "Example: query.php?what=langlinks&titles=MediaWiki|Wikipedia", |
145 | 170 | )), |
146 | | - 'templates' => array( "genPageTemplates", false, null, array( |
| 171 | + 'templates' => array( "genPageTemplates", false, null, null, array( |
147 | 172 | "List of used templates", |
148 | 173 | "Example: query.php?what=templates&titles=Main%20Page", |
149 | 174 | )), |
150 | | - 'backlinks' => array( "genPageBackLinks", false, array('blfilter','bllimit','bloffset'), array( |
| 175 | + 'backlinks' => array( "genPageBackLinksHelper", false, |
| 176 | + array('blfilter', 'bllimit', 'bloffset'), |
| 177 | + array(array('existing', 'nonredirects', 'all'), 50, null), |
| 178 | + array( |
151 | 179 | "What pages link to this page(s)", |
152 | 180 | "Parameters supported:", |
153 | 181 | "blfilter - Of all given pages, which should be queried:", |
— | — | @@ -155,7 +183,10 @@ |
156 | 184 | "bloffset - when too many results are found, use this to page", |
157 | 185 | "Example: query.php?what=backlinks&titles=Main%20Page&bllimit=10", |
158 | 186 | )), |
159 | | - 'embeddedin' => array( "genPageEmbeddedIn", false, array('eifilter','eilimit','eioffset'), array( |
| 187 | + 'embeddedin' => array( "genPageBackLinksHelper", false, |
| 188 | + array('eifilter', 'eilimit', 'eioffset'), |
| 189 | + array(array('existing', 'nonredirects', 'all'), 50, null), |
| 190 | + array( |
160 | 191 | "What pages include this page(s) as template(s)", |
161 | 192 | "Parameters supported:", |
162 | 193 | "eifilter - Of all given pages, which should be queried:", |
— | — | @@ -166,12 +197,28 @@ |
167 | 198 | " Page 1: query.php?what=embeddedin&titles=Template:Stub&eilimit=10", |
168 | 199 | " Page 2: query.php?what=embeddedin&titles=Template:Stub&eilimit=10&eioffset=10", |
169 | 200 | )), |
170 | | - 'revisions' => array( "genPageHistory", false, array('rvcomments','rvlimit','rvoffset'), array( |
| 201 | + 'imagelinks' => array( "genPageBackLinksHelper", false, |
| 202 | + array('ilfilter', 'illimit', 'iloffset'), |
| 203 | + array(array('existing', 'nonredirects', 'all'), 50, null), |
| 204 | + array( |
| 205 | + "What pages use this image(s)", |
| 206 | + "ilfilter - Of all given images, which should be queried:", |
| 207 | + " 'nonredirects', 'existing' (default), or 'all' (including non-existant)", |
| 208 | + "illimit - how many total links to return", |
| 209 | + "iloffset - when too many results are found, use this to page", |
| 210 | + "Example: query.php?what=imagelinks&titles=image:test.jpg&illimit=10", |
| 211 | + )), |
| 212 | + 'revisions' => array( "genPageHistory", false, |
| 213 | + array('rvcomments', 'rvlimit', 'rvoffset', 'rvstart', 'rvend'), |
| 214 | + array(null, 50, null, null, null), |
| 215 | + array( |
171 | 216 | "Revision history - Lists edits performed to the given pages", |
172 | 217 | "Parameters supported:", |
173 | 218 | "rvcomments - if specified, the result will include summary strings", |
174 | 219 | "rvlimit - how many links to return *for each title*", |
175 | 220 | "rvoffset - when too many results are found, use this to page", |
| 221 | + "rvstart - timestamp of the earliest entry", |
| 222 | + "rvend - timestamp of the latest entry", |
176 | 223 | "Example: query.php?what=revisions&titles=Main%20Page&rvlimit=10&rvcomments", |
177 | 224 | )), |
178 | 225 | ); |
— | — | @@ -182,10 +229,13 @@ |
183 | 230 | $this->data = array(); |
184 | 231 | $this->requestsize = 0; |
185 | 232 | $this->db = $db; |
186 | | - $this->format = 'html'; // set it here because if parseFormat fails, it should still output something |
| 233 | + |
| 234 | + $this->format = 'html'; // set it here because if parseFormat fails, the usage output rilies on this variable |
187 | 235 | $this->format = $this->parseFormat( $wgRequest->getVal('format', 'html') ); |
188 | | - $this->properties = $this->parseMultiValue( 'what', null, array_keys( $this->propGenerators ) ); |
189 | 236 | |
| 237 | + $allProperties = array_merge(array(null), array_keys( $this->propGenerators )); |
| 238 | + $this->properties = $this->parseMultiValue( 'what', $allProperties ); |
| 239 | + |
190 | 240 | // Neither one of these variables is referenced directly! |
191 | 241 | // Meta generators may append titles or pageids to these varibales. |
192 | 242 | // Do not modify this values directly - use the AddRaw() method |
— | — | @@ -213,7 +263,7 @@ |
214 | 264 | function callGenerators( $callMetaGenerators ) { |
215 | 265 | foreach( $this->propGenerators as $property => &$generator ) { |
216 | 266 | if( $generator[GEN_ISMETA] === $callMetaGenerators && in_array( $property, $this->properties )) { |
217 | | - $this->{$generator[GEN_FUNCTION]}(); |
| 267 | + $this->{$generator[GEN_FUNCTION]}($property, $generator); |
218 | 268 | } |
219 | 269 | } |
220 | 270 | } |
— | — | @@ -263,10 +313,10 @@ |
264 | 314 | } |
265 | 315 | } |
266 | 316 | |
267 | | - function parseMultiValue( $valueName, $defaultValue, $allowedValues ) { |
| 317 | + function parseMultiValue( $valueName, $allowedValues ) { |
268 | 318 | global $wgRequest; |
269 | 319 | |
270 | | - $values = $wgRequest->getVal($valueName, $defaultValue); |
| 320 | + $values = $wgRequest->getVal($valueName, $allowedValues[0]); |
271 | 321 | $valuesList = explode( '|', $values ); |
272 | 322 | $unknownValues = array_diff( $valuesList, $allowedValues); |
273 | 323 | if( $unknownValues ) { |
— | — | @@ -339,15 +389,7 @@ |
340 | 390 | // |
341 | 391 | // User restrictions |
342 | 392 | // |
343 | | - if( $wgUser->isBot() ) { |
344 | | - if ( $this->requestsize > 1000 ) { |
345 | | - $this->dieUsage( 'Bots may not request over 1000 pages', 'pi_botquerytoobig' ); |
346 | | - } |
347 | | - } else { |
348 | | - if( $this->requestsize > 20 ) { |
349 | | - $this->dieUsage( 'Users may not request over 20 pages', 'pi_userquerytoobig' ); |
350 | | - } |
351 | | - } |
| 393 | + $this->validateLimit( 'pi_botquerytoobig', $this->requestsize, 50, 1000 ); |
352 | 394 | |
353 | 395 | // |
354 | 396 | // Make sure that this->data['pages'] is empty |
— | — | @@ -440,7 +482,7 @@ |
441 | 483 | return true; // success |
442 | 484 | } |
443 | 485 | |
444 | | - function genMetaSiteInfo() { |
| 486 | + function genMetaSiteInfo(&$prop, &$genInfo) { |
445 | 487 | global $wgSitename, $wgVersion, $wgCapitalLinks; |
446 | 488 | $meta = array(); |
447 | 489 | $mainPage = Title::newFromText( wfMsgForContent( 'mainpage' ) ); |
— | — | @@ -454,7 +496,7 @@ |
455 | 497 | $this->data['meta']['site'] = $meta; |
456 | 498 | } |
457 | 499 | |
458 | | - function genMetaNamespaceInfo() { |
| 500 | + function genMetaNamespaceInfo(&$prop, &$genInfo) { |
459 | 501 | global $wgContLang; |
460 | 502 | $meta = array(); |
461 | 503 | $meta['_element'] = 'ns'; |
— | — | @@ -464,7 +506,7 @@ |
465 | 507 | $this->data['meta']['namespaces'] = $meta; |
466 | 508 | } |
467 | 509 | |
468 | | - function genMetaUserInfo() { |
| 510 | + function genMetaUserInfo(&$prop, &$genInfo) { |
469 | 511 | global $wgUser; |
470 | 512 | |
471 | 513 | $meta = array(); |
— | — | @@ -479,31 +521,21 @@ |
480 | 522 | $this->data['meta']['user'] = $meta; |
481 | 523 | } |
482 | 524 | |
483 | | - function genMetaRecentChanges() { |
484 | | - global $wgRequest; |
| 525 | + function genMetaRecentChanges(&$prop, &$genInfo) { |
485 | 526 | |
486 | | - # Get last modified date, for client caching |
487 | | - $from = $wgRequest->getVal( 'rcfrom' ); |
488 | | - $limit = $wgRequest->getInt( 'rclimit', 20 ); |
489 | | - $hide = $this->parseMultiValue( 'rchide', '', array('','minor','bots','anons','liu') ); |
490 | | - |
| 527 | + extract( $this->getParams( $prop, $genInfo )); |
491 | 528 | # It makes no sense to hide both anons and logged-in users |
492 | | - # Where this occurs, force anons to be shown |
493 | | - if( in_array('anons', $hide) && in_array('liu', $hide) ) { |
| 529 | + if( in_array('anons', $rchide) && in_array('liu', $rchide) ) { |
494 | 530 | $this->dieUsage( "Both 'anons' and 'liu' cannot be given for 'rchide' parameter", 'rc_badrchide' ); |
495 | 531 | } |
| 532 | + $this->validateLimit( 'rc_badrclimit', $rclimit, 100, 5000 ); |
496 | 533 | |
497 | | - $conds = array(); |
498 | | - |
499 | | - if ( $from != '' ) { |
500 | | - $conds[] = 'rev_timestamp >= ' . $this->prepareTimestamp($from); |
| 534 | + $conds = array(); |
| 535 | + if ( $rcfrom != '' ) { |
| 536 | + $conds[] = 'rev_timestamp >= ' . $this->prepareTimestamp($rcfrom); |
501 | 537 | } |
502 | 538 | |
503 | | - if ( $limit < 1 || $limit > 5000 ) { |
504 | | - $this->dieUsage( "Invalid rclimit value '$limit' - must be between 1 and 5000", 'rc_badrclimit' ); |
505 | | - } |
506 | | - |
507 | | - foreach( $hide as &$elem ) { |
| 539 | + foreach( $rchide as &$elem ) { |
508 | 540 | switch( $elem ) { |
509 | 541 | case '': // nothing |
510 | 542 | break; |
— | — | @@ -524,8 +556,8 @@ |
525 | 557 | } |
526 | 558 | } |
527 | 559 | |
528 | | - $options = array( 'USE INDEX' => 'rc_timestamp', 'LIMIT' => $limit ); |
529 | | - $options['ORDER BY'] = 'rc_timestamp' . ( $from != '' ? '' : ' DESC' ); |
| 560 | + $options = array( 'USE INDEX' => 'rc_timestamp', 'LIMIT' => $rclimit ); |
| 561 | + $options['ORDER BY'] = 'rc_timestamp' . ( $rcfrom != '' ? '' : ' DESC' ); |
530 | 562 | |
531 | 563 | $res = $this->db->select( |
532 | 564 | 'recentchanges', |
— | — | @@ -542,34 +574,56 @@ |
543 | 575 | $this->db->freeResult( $res ); |
544 | 576 | } |
545 | 577 | |
546 | | - function genMetaDoubleRedirects() { |
547 | | - global $wgRequest, $wgUser; |
| 578 | + function genUserPages(&$prop, &$genInfo) { |
| 579 | + global $wgContLang; |
| 580 | + |
| 581 | + extract( $this->getParams( $prop, $genInfo )); |
548 | 582 | |
| 583 | + $res = $this->db->select( |
| 584 | + 'user', |
| 585 | + 'user_name', |
| 586 | + "user_name >= '$usfrom'", |
| 587 | + $this->classname . '::genUserPages', |
| 588 | + array( 'ORDER BY' => 'user_name', 'LIMIT' => $uslimit ) |
| 589 | + ); |
| 590 | + |
| 591 | + $userNS = $wgContLang->getNsText(NS_USER); |
| 592 | + if( !$userNS ) $userNS = 'User'; |
| 593 | + $userNS .= ':'; |
| 594 | + |
| 595 | + while ( $row = $this->db->fetchObject( $res ) ) { |
| 596 | + $this->addRaw( 'titles', $userNS . $row->user_name ); |
| 597 | + } |
| 598 | + $this->db->freeResult( $res ); |
| 599 | + } |
| 600 | + |
| 601 | + function genMetaDoubleRedirects(&$prop, &$genInfo) { |
| 602 | + global $wgUser; |
| 603 | + |
549 | 604 | $this->dieUsage( "DoubleRedirect generator is disabled until caching is implemented", 'dr_disabled' ); |
550 | 605 | |
551 | 606 | if( !$wgUser->isBot() ) { |
552 | 607 | $this->dieUsage( "Only bots are allowed to query for double-redirects", 'dr_notbot' ); |
553 | 608 | } |
554 | 609 | |
| 610 | + extract( $this->getParams( $prop, $genInfo )); |
555 | 611 | extract( $this->db->tableNames( 'page', 'pagelinks' ) ); |
556 | 612 | |
557 | | - $offset = $wgRequest->getInt( 'droffset', 0 ); |
558 | | - $limit = $wgRequest->getInt( 'drlimit', 50 ); |
559 | 613 | $sql = "SELECT " . |
560 | | - " pa.page_id id_a," . |
561 | | - " pb.page_id id_b," . |
562 | | - " pc.page_id id_c" . |
| 614 | + " pa.page_id id_a," . |
| 615 | + " pb.page_id id_b," . |
| 616 | + " pc.page_id id_c" . |
563 | 617 | " FROM $pagelinks AS la, $pagelinks AS lb, $page AS pa, $page AS pb, $page AS pc" . |
564 | 618 | " WHERE pa.page_is_redirect=1 AND pb.page_is_redirect=1" . |
565 | | - " AND la.pl_from=pa.page_id" . |
566 | | - " AND la.pl_namespace=pb.page_namespace" . |
567 | | - " AND la.pl_title=pb.page_title" . |
568 | | - " AND lb.pl_from=pb.page_id" . |
569 | | - " AND lb.pl_namespace=pc.page_namespace" . |
570 | | - " AND lb.pl_title=pc.page_title" . |
571 | | - " LIMIT $limit"; |
572 | | - if( $offset > 0 ) { |
573 | | - $sql .= " OFFSET $offset"; |
| 619 | + " AND la.pl_from=pa.page_id" . |
| 620 | + " AND la.pl_namespace=pb.page_namespace" . |
| 621 | + " AND la.pl_title=pb.page_title" . |
| 622 | + " AND lb.pl_from=pb.page_id" . |
| 623 | + " AND lb.pl_namespace=pc.page_namespace" . |
| 624 | + " AND lb.pl_title=pc.page_title" . |
| 625 | + " LIMIT $drlimit"; |
| 626 | + if( isset($droffset) ) { |
| 627 | + $sql .= " OFFSET $droffset"; |
574 | 628 | } |
575 | 629 | |
576 | 630 | // Add found page ids to the list of requested ids - they will be auto-populated later |
— | — | @@ -581,7 +635,7 @@ |
582 | 636 | $this->db->freeResult( $res ); |
583 | 637 | } |
584 | 638 | |
585 | | - function genPageLangLinks() { |
| 639 | + function genPageLangLinks(&$prop, &$genInfo) { |
586 | 640 | if( !$this->nonRedirPageIds ) { |
587 | 641 | return; |
588 | 642 | } |
— | — | @@ -596,7 +650,7 @@ |
597 | 651 | $this->db->freeResult( $res ); |
598 | 652 | } |
599 | 653 | |
600 | | - function genPageTemplates() { |
| 654 | + function genPageTemplates(&$prop, &$genInfo) { |
601 | 655 | if( !$this->nonRedirPageIds ) { |
602 | 656 | return; |
603 | 657 | } |
— | — | @@ -630,24 +684,15 @@ |
631 | 685 | $this->addPageSubElement( $row->pl_from, 'links', 'l', $this->getLinkInfo( $row->pl_namespace, $row->pl_title )); |
632 | 686 | } |
633 | 687 | $this->db->freeResult( $res ); |
634 | | - } |
635 | | - |
636 | | - function genPageBackLinks() { |
637 | | - $this->genPageBackLinksHelper( 'backlinks' ); |
638 | | - } |
| 688 | + } |
639 | 689 | |
640 | | - function genPageEmbeddedIn() { |
641 | | - $this->genPageBackLinksHelper( 'embeddedin' ); |
642 | | - } |
643 | | - |
644 | 690 | /** |
645 | 691 | * Generate backlinks for either links, templates, or both |
646 | 692 | * $type - either 'template' or 'page' |
647 | 693 | */ |
648 | | - function genPageBackLinksHelper( $type ) { |
649 | | - global $wgRequest; |
650 | | - |
651 | | - switch( $type ) { |
| 694 | + function genPageBackLinksHelper(&$prop, &$genInfo) { |
| 695 | + $isImage = false; |
| 696 | + switch( $prop ) { |
652 | 697 | case 'embeddedin' : |
653 | 698 | $columnPrefix = 'tl'; // database column name prefix |
654 | 699 | $code = 'ei'; // |
— | — | @@ -658,15 +703,27 @@ |
659 | 704 | $code = 'bl'; |
660 | 705 | $linktbl = $this->db->tableName( 'pagelinks' ); |
661 | 706 | break; |
| 707 | + case 'imagelinks' : |
| 708 | + $columnPrefix = 'il'; |
| 709 | + $code = 'il'; |
| 710 | + $linktbl = $this->db->tableName( 'imagelinks' ); |
| 711 | + $isImage = true; |
| 712 | + break; |
662 | 713 | default : |
663 | 714 | die("unknown type"); |
664 | 715 | } |
665 | 716 | $pagetbl = $this->db->tableName( 'page' ); |
666 | | - |
667 | | - $offset = $wgRequest->getInt( "{$code}offset", 0 ); |
668 | | - $limit = $wgRequest->getInt( "{$code}limit", 50 ) + 1; |
669 | | - $filter = $wgRequest->getVal( "{$code}filter", 'existing' ); |
670 | 717 | |
| 718 | + $parameters = $this->getParams( $prop, $genInfo ); |
| 719 | + $offset = $parameters["{$code}offset"]; |
| 720 | + $limit = $parameters["{$code}limit"] + 1; |
| 721 | + $filter = $parameters["{$code}filter"]; |
| 722 | + if( count($filter) != 1 ) { |
| 723 | + $this->dieUsage( "{$code}filter must either be 'all', 'existing', or 'nonredirects'", "{$code}_badmultifilter" ); |
| 724 | + } else { |
| 725 | + $filter = $filter[0]; |
| 726 | + } |
| 727 | + |
671 | 728 | $nonredir = $existing = $all = false; |
672 | 729 | switch( $filter ) { |
673 | 730 | case 'all' : |
— | — | @@ -684,32 +741,59 @@ |
685 | 742 | |
686 | 743 | $linkBatch = new LinkBatch; |
687 | 744 | foreach( $this->data['pages'] as $key => &$page ) { |
688 | | - if(( $key < 0 && $all && array_key_exists('_obj', $page) ) || |
689 | | - ( $key > 0 && ($existing || ($nonredir && !array_key_exists('redirect', $page))) )) { |
690 | | - |
| 745 | + if( ( |
| 746 | + ( $key < 0 && $all && array_key_exists('_obj', $page) ) || |
| 747 | + ( $key > 0 && ($existing || ($nonredir && !array_key_exists('redirect', $page))) ) |
| 748 | + ) |
| 749 | + && |
| 750 | + ( !$isImage || $page['ns'] == NS_IMAGE ) // when doing image links search, only allow NS_IMAGE |
| 751 | + ) { |
691 | 752 | $linkBatch->addObj( $page['_obj'] ); |
692 | 753 | } |
693 | 754 | } |
694 | 755 | |
695 | 756 | if( $linkBatch->isEmpty() ) { |
696 | | - $this->addStatusMessage( $type, 'emptyrequest' ); |
| 757 | + $this->addStatusMessage( $prop, 'emptyrequest' ); |
697 | 758 | return; // Nothing to do |
698 | 759 | } |
| 760 | + |
| 761 | + if( $isImage ) { |
| 762 | + $where = "{$columnPrefix}_to IN ("; |
| 763 | + $firstTitle = true; |
| 764 | + foreach( $linkBatch->data[NS_IMAGE] as $dbkey => $nothing ) { |
| 765 | + if ( $firstTitle ) { |
| 766 | + $firstTitle = false; |
| 767 | + } else { |
| 768 | + $where .= ','; |
| 769 | + } |
| 770 | + $where .= $this->db->addQuotes( $dbkey ); |
| 771 | + } |
| 772 | + $where .= ')'; |
| 773 | + } else { |
| 774 | + $where = $linkBatch->constructSet( $columnPrefix, $this->db ); |
| 775 | + } |
699 | 776 | |
700 | 777 | $sql = "SELECT" |
701 | | - ." pfrom.page_id from_id, pfrom.page_namespace from_namespace, pfrom.page_title from_title," |
702 | | - ." pto.page_id to_id, {$columnPrefix}_namespace to_namespace, {$columnPrefix}_title to_title" |
| 778 | + ." pfrom.page_id from_id, pfrom.page_namespace from_namespace," |
| 779 | + ." pfrom.page_title from_title, pto.page_id to_id," |
| 780 | + .($isImage ? |
| 781 | + " {$columnPrefix}_to to_title" : |
| 782 | + " {$columnPrefix}_namespace to_namespace, {$columnPrefix}_title to_title" ) |
703 | 783 | ." FROM" |
704 | 784 | ." (" |
705 | | - ." $linktbl INNER JOIN $pagetbl pfrom ON {$columnPrefix}_from = pfrom.page_id" |
| 785 | + ." $linktbl INNER JOIN $pagetbl pfrom ON {$columnPrefix}_from = pfrom.page_id" |
706 | 786 | ." )" |
707 | | - ." LEFT JOIN $pagetbl pto ON {$columnPrefix}_namespace = pto.page_namespace AND {$columnPrefix}_title = pto.page_title" |
708 | | - ." WHERE" |
709 | | - ." " . $linkBatch->constructSet( $columnPrefix, $this->db ) |
| 787 | + ." LEFT JOIN $pagetbl pto ON" |
| 788 | + .($isImage ? |
| 789 | + " {$columnPrefix}_to = pto.page_title" : |
| 790 | + " {$columnPrefix}_title = pto.page_title AND {$columnPrefix}_namespace = pto.page_namespace") |
| 791 | + ." WHERE $where" |
710 | 792 | ." ORDER BY" |
711 | | - ." {$columnPrefix}_namespace, {$columnPrefix}_title" |
| 793 | + .($isImage ? |
| 794 | + " {$columnPrefix}_to" : |
| 795 | + " {$columnPrefix}_namespace, {$columnPrefix}_title") |
712 | 796 | ." LIMIT $limit" |
713 | | - . ( $offset > 0 ? " OFFSET $offset" : "" ); |
| 797 | + . ( isset($offset) ? " OFFSET $offset" : "" ); |
714 | 798 | |
715 | 799 | $count = 0; |
716 | 800 | $res = $this->db->query( $sql, $this->classname . "::genPageBackLinks_{$code}" ); |
— | — | @@ -721,56 +805,48 @@ |
722 | 806 | } |
723 | 807 | $pageId = $row->to_id; |
724 | 808 | if( $pageId === null ) { |
725 | | - $pageId = $this->lookupInvalidPageId( $row->to_namespace, $row->to_title ); |
| 809 | + $pageId = $this->lookupInvalidPageId( |
| 810 | + $isImage ? NS_IMAGE : $row->to_namespace, |
| 811 | + $row->to_title ); |
726 | 812 | } |
727 | 813 | $values = $this->getLinkInfo( $row->from_namespace, $row->from_title, $row->from_id ); |
728 | | - $this->addPageSubElement( $pageId, $type, $code, $values ); |
| 814 | + $this->addPageSubElement( $pageId, $prop, $code, $values ); |
729 | 815 | } |
730 | 816 | $this->db->freeResult( $res ); |
731 | 817 | |
732 | 818 | if( $count < $limit ) { |
733 | | - $this->addStatusMessage( $type, 'done' ); |
| 819 | + $this->addStatusMessage( $prop, 'done' ); |
734 | 820 | } else { |
735 | | - $this->addStatusMessage( $type, 'havemore' ); |
| 821 | + $this->addStatusMessage( $prop, 'havemore' ); |
736 | 822 | } |
737 | 823 | } |
738 | 824 | |
739 | | - function genPageHistory() { |
740 | | - global $wgRequest; |
741 | | - |
| 825 | + function genPageHistory(&$prop, &$genInfo) { |
742 | 826 | if( !$this->existingPageIds ) { |
743 | 827 | return; |
744 | 828 | } |
| 829 | + extract( $this->getParams( $prop, $genInfo )); |
745 | 830 | |
746 | | - $includeComments = $wgRequest->getCheck('rvcomments'); |
747 | | - |
748 | 831 | // select *: rev_page, rev_text_id, rev_comment, rev_user, rev_user_text, rev_timestamp, rev_minor_edit, rev_deleted |
749 | | - $fields = array('rev_id','rev_timestamp','rev_user','rev_user_text','rev_minor_edit'); |
750 | | - if( $includeComments ) { |
| 832 | + $fields = array('rev_id', 'rev_timestamp', 'rev_user', 'rev_user_text', 'rev_minor_edit'); |
| 833 | + if( isset($rvcomments) ) { |
751 | 834 | $fields[] = 'rev_comment'; |
752 | 835 | } |
753 | | - |
754 | | - $conds = array( |
755 | | - 'rev_deleted' => 0, |
756 | | - ); |
757 | | - |
758 | | - $start = $wgRequest->getVal( 'rvstart' ); |
759 | | - if ( $start != '' ) { |
760 | | - $conds[] = 'rev_timestamp >= ' . $this->prepareTimestamp($start); |
| 836 | + $conds = array( 'rev_deleted' => 0 ); |
| 837 | + if ( isset($rvstart) ) { |
| 838 | + $conds[] = 'rev_timestamp >= ' . $this->prepareTimestamp($rvstart); |
761 | 839 | } |
762 | | - |
763 | | - $end = $wgRequest->getVal( 'rvend' ); |
764 | | - if ( $end != '' ) { |
765 | | - $conds[] = 'rev_timestamp <= ' . $this->prepareTimestamp($end); |
| 840 | + if ( isset($rvend) ) { |
| 841 | + $conds[] = 'rev_timestamp <= ' . $this->prepareTimestamp($rvend); |
766 | 842 | } |
767 | | - |
768 | | - $limit = $wgRequest->getInt( 'rvlimit', 50 ); |
769 | 843 | $options = array( |
770 | | - 'LIMIT' => $limit, |
| 844 | + 'LIMIT' => $rvlimit, |
771 | 845 | 'ORDER BY' => 'rev_timestamp DESC' |
772 | 846 | ); |
773 | | - |
774 | | - if( $limit * count($this->existingPageIds) > 20000 ) { |
| 847 | + if( isset($rvoffset) ) { |
| 848 | + $options['OFFSET'] = $rvoffset; |
| 849 | + } |
| 850 | + if( $rvlimit * count($this->existingPageIds) > 20000 ) { |
775 | 851 | $this->dieUsage( "rvlimit multiplied by number of requested titles must be less than 20000", 'rv_querytoobig' ); |
776 | 852 | } |
777 | 853 | |
— | — | @@ -789,7 +865,7 @@ |
790 | 866 | if( $row->rev_minor_edit ) { |
791 | 867 | $vals['minor'] = ''; |
792 | 868 | } |
793 | | - $vals['*'] = $includeComments ? $row->rev_comment : ''; |
| 869 | + $vals['*'] = $rvcomments ? $row->rev_comment : ''; |
794 | 870 | $this->addPageSubElement( $pageId, 'revisions', 'rv', $vals); |
795 | 871 | } |
796 | 872 | $this->db->freeResult( $res ); |
— | — | @@ -800,6 +876,39 @@ |
801 | 877 | // ************************************* UTILITIES ************************************* |
802 | 878 | // |
803 | 879 | |
| 880 | + /** |
| 881 | + * From two parameter arrays, makes an array of the values provided by the user. |
| 882 | + */ |
| 883 | + function getParams( &$property, &$generator ) { |
| 884 | + global $wgRequest; |
| 885 | + |
| 886 | + $paramNames = &$generator[GEN_PARAMS]; |
| 887 | + $paramDefaults = &$generator[GEN_DEFAULTS]; |
| 888 | + if( count($paramNames) !== count($paramDefaults) ) { |
| 889 | + die("Internal error: '$property' param count mismatch"); |
| 890 | + } |
| 891 | + $results = array(); |
| 892 | + for( $i = 0; $i < count($paramNames); $i++ ) { |
| 893 | + $param = &$paramNames[$i]; |
| 894 | + $dflt = &$paramDefaults[$i]; |
| 895 | + switch( gettype($dflt) ) { |
| 896 | + case 'NULL': |
| 897 | + case 'string': |
| 898 | + $result = $wgRequest->getVal( $param, $dflt ); |
| 899 | + break; |
| 900 | + case 'integer': |
| 901 | + $result = $wgRequest->getInt( $param, $dflt ); |
| 902 | + break; |
| 903 | + case 'array': |
| 904 | + $result = $this->parseMultiValue( $param, $dflt ); |
| 905 | + break; |
| 906 | + default: |
| 907 | + die('Internal error: unprocessed type ' . gettype($dflt)); |
| 908 | + } |
| 909 | + $results[$param] = $result; |
| 910 | + } |
| 911 | + return $results; |
| 912 | + } |
804 | 913 | |
805 | 914 | /** |
806 | 915 | * Lookup of the page id by ns:title in the data array. Very slow - lookup by id if possible. |
— | — | @@ -986,6 +1095,24 @@ |
987 | 1096 | } |
988 | 1097 | } |
989 | 1098 | } |
| 1099 | + |
| 1100 | + function validateLimit( $varname, &$value, $max, $botMax = false, $min = 1 ) { |
| 1101 | + global $wgUser; |
| 1102 | + if( !$botMax ) $botMax = $max; |
| 1103 | + |
| 1104 | + if ( $value < $min ) { |
| 1105 | + $this->dieUsage( "Minimum cannot be less than $min", $varname ); |
| 1106 | + } |
| 1107 | + if( $wgUser->isBot() ) { |
| 1108 | + if ( $value > $botMax ) { |
| 1109 | + $this->dieUsage( "Bots may not request over $botMax pages", $varname ); |
| 1110 | + } |
| 1111 | + } else { |
| 1112 | + if( $this->requestsize > $max ) { |
| 1113 | + $this->dieUsage( "Users may not request over $max pages", $varname ); |
| 1114 | + } |
| 1115 | + } |
| 1116 | + } |
990 | 1117 | } |
991 | 1118 | |
992 | 1119 | // |
— | — | @@ -1036,7 +1163,6 @@ |
1037 | 1164 | function echoprinter( $text ) { |
1038 | 1165 | echo $text; |
1039 | 1166 | } |
1040 | | - |
1041 | 1167 | function printHumanReadable( &$data ) { |
1042 | 1168 | sanitizeOutputData($data); |
1043 | 1169 | print_r($data); |
— | — | @@ -1154,4 +1280,5 @@ |
1155 | 1281 | } |
1156 | 1282 | return $params; |
1157 | 1283 | } |
| 1284 | + |
1158 | 1285 | ?> |