Index: trunk/extensions/BotQuery/query.php |
— | — | @@ -85,19 +85,23 @@ |
86 | 86 | "Example: query.php?what=info&format=html", |
87 | 87 | )), |
88 | 88 | 'txt' => array( 'printHumanReadable', 'application/x-wiki-botquery-print_r', null, null, array( |
89 | | - "Human-readable format using print_r() (http://www.php.net/print_r)", |
| 89 | + "Human-readable format using print_r()", |
| 90 | + "Details: http://www.php.net/print_r", |
90 | 91 | "Example: query.php?what=info&format=txt", |
91 | 92 | )), |
92 | 93 | 'json'=> array( 'printJSON', 'application/json', null, null, array( |
93 | | - "JSON format (http://en.wikipedia.org/wiki/JSON)", |
| 94 | + "JSON format", |
| 95 | + "Details: http://en.wikipedia.org/wiki/JSON", |
94 | 96 | "Example: query.php?what=info&format=json", |
95 | 97 | )), |
96 | 98 | 'php' => array( 'printPHP', 'application/vnd.php.serialized', null, null, array( |
97 | | - "PHP serialized format using serialize() (http://www.php.net/serialize)", |
| 99 | + "PHP serialized format using serialize()", |
| 100 | + "Details: http://www.php.net/serialize", |
98 | 101 | "Example: query.php?what=info&format=php", |
99 | 102 | )), |
100 | | - 'dbg' => array( 'printParsableCode', 'application/x-wiki-botquery-var_export', null, null, array( |
101 | | - "PHP source code format using var_export() (http://www.php.net/var_export)", |
| 103 | + 'dbg' => array( 'printDebugCode', 'application/x-wiki-botquery-var_export', null, null, array( |
| 104 | + "PHP source code format using var_export()", |
| 105 | + "Details: http://www.php.net/var_export", |
102 | 106 | "Example: query.php?what=info&format=dbg", |
103 | 107 | )), |
104 | 108 | ); |
— | — | @@ -113,19 +117,24 @@ |
114 | 118 | var $propGenerators = array( |
115 | 119 | |
116 | 120 | // Site-wide Generators |
117 | | - 'info' => array( "genMetaSiteInfo", true, null, null, array( |
| 121 | + 'info' => array( 'genMetaSiteInfo', true, null, null, array( |
118 | 122 | "General site information", |
119 | 123 | "Example: query.php?what=info", |
120 | 124 | )), |
121 | | - 'namespaces' => array( "genMetaNamespaceInfo", true, null, null, array( |
| 125 | + 'namespaces' => array( 'genMetaNamespaceInfo', true, null, null, array( |
122 | 126 | "List of localized namespace names", |
123 | 127 | "Example: query.php?what=namespaces", |
124 | 128 | )), |
125 | | - 'userinfo' => array( "genMetaUserInfo", true, null, null, array( |
| 129 | + 'userinfo' => array( 'genMetaUserInfo', true, |
| 130 | + array( 'uiextended' ), |
| 131 | + array( false ), |
| 132 | + array( |
126 | 133 | "Information about current user", |
127 | | - "Example: query.php?what=userinfo", |
| 134 | + "Parameters supported:", |
| 135 | + "uiextended - If present, includes additional information such as rights and groups.", |
| 136 | + "Example: query.php?what=userinfo&uiextended", |
128 | 137 | )), |
129 | | - 'recentchanges' => array( "genMetaRecentChanges", true, |
| 138 | + 'recentchanges' => array( 'genMetaRecentChanges', true, |
130 | 139 | array( 'rcfrom', 'rclimit', 'rchide' ), |
131 | 140 | array( null, 50, array(null, 'minor', 'bots', 'anons', 'liu') ), |
132 | 141 | array( |
— | — | @@ -138,7 +147,7 @@ |
139 | 148 | " Cannot specify both anons and liu.", |
140 | 149 | "Example: query.php?what=recentchanges&rchide=liu|bots", |
141 | 150 | )), |
142 | | - 'allpages' => array( "genMetaAllPages", true, |
| 151 | + 'allpages' => array( 'genMetaAllPages', true, |
143 | 152 | array( 'aplimit', 'apfrom', 'apnamespace' ), |
144 | 153 | array( 50, '!', 0 ), |
145 | 154 | array( |
— | — | @@ -149,17 +158,18 @@ |
150 | 159 | "apnamespaces - limits which namespace to enumerate. Default 0 (Main)", |
151 | 160 | "Example: query.php?what=allpages&aplimit=50", |
152 | 161 | )), |
153 | | - 'nolanglinks' => array( "genMetaNoLangLinksPages", true, |
154 | | - array( 'nllimit', 'nlfromid' ), |
155 | | - array( 50, 0 ), |
| 162 | + 'nolanglinks' => array( 'genMetaNoLangLinksPages', true, |
| 163 | + array( 'nllimit', 'nlfrom', 'nlnamespace' ), |
| 164 | + array( 50, '!', 0 ), |
156 | 165 | array( |
157 | 166 | "Enumerates pages without language links to the output list.", |
158 | 167 | "Parameters supported:", |
159 | 168 | "nllimit - how many total pages to return", |
160 | | - "nlfromid - the page id to start enumerating from. Default is 0", |
| 169 | + "nlfrom - the page title to start enumerating from. Default is '!'", |
| 170 | + "nlnamespaces - limits which namespace to enumerate. Default 0 (Main)", |
161 | 171 | "Example: query.php?what=nolanglinks&nllimit=50", |
162 | 172 | )), |
163 | | - 'users' => array( "genUserPages", true, |
| 173 | + 'users' => array( 'genUserPages', true, |
164 | 174 | array( 'usfrom', 'uslimit' ), |
165 | 175 | array( null, 50 ), |
166 | 176 | array( |
— | — | @@ -173,24 +183,24 @@ |
174 | 184 | // |
175 | 185 | // Page-specific Generators |
176 | 186 | // |
177 | | - 'redirects' => array( "genRedirectInfo", false, null, null, array( |
| 187 | + 'redirects' => array( 'genRedirectInfo', false, null, null, array( |
178 | 188 | "For all given redirects, provides additional information such as pageIds and double-redirection", |
179 | 189 | "Example: query.php?what=redirects&titles=Main_page", |
180 | 190 | " query.php?what=recentchanges|redirects (Which of the recent changes are redirects?)", |
181 | 191 | )), |
182 | | - 'links' => array( "genPageLinksHelper", false, null, null, array( |
| 192 | + 'links' => array( 'genPageLinksHelper', false, null, null, array( |
183 | 193 | "List of regular page links", |
184 | 194 | "Example: query.php?what=links&titles=MediaWiki|Wikipedia", |
185 | 195 | )), |
186 | | - 'langlinks' => array( "genPageLinksHelper", false, null, null, array( |
| 196 | + 'langlinks' => array( 'genPageLinksHelper', false, null, null, array( |
187 | 197 | "Inter-language links", |
188 | 198 | "Example: query.php?what=langlinks&titles=MediaWiki|Wikipedia", |
189 | 199 | )), |
190 | | - 'templates' => array( "genPageLinksHelper", false, null, null, array( |
| 200 | + 'templates' => array( 'genPageLinksHelper', false, null, null, array( |
191 | 201 | "List of used templates", |
192 | 202 | "Example: query.php?what=templates&titles=Main_Page", |
193 | 203 | )), |
194 | | - 'backlinks' => array( "genPageBackLinksHelper", false, |
| 204 | + 'backlinks' => array( 'genPageBackLinksHelper', false, |
195 | 205 | array( 'blfilter', 'bllimit', 'blcontfrom' ), |
196 | 206 | array( array('existing', 'nonredirects', 'all'), 50, null ), |
197 | 207 | array( |
— | — | @@ -202,7 +212,7 @@ |
203 | 213 | "blcontfrom - from which point to continue. Use the 'next' value from previous queries.", |
204 | 214 | "Example: query.php?what=backlinks&titles=Main%20Page&bllimit=10", |
205 | 215 | )), |
206 | | - 'embeddedin' => array( "genPageBackLinksHelper", false, |
| 216 | + 'embeddedin' => array( 'genPageBackLinksHelper', false, |
207 | 217 | array( 'eifilter', 'eilimit', 'eicontfrom' ), |
208 | 218 | array( array('existing', 'nonredirects', 'all'), 50, null ), |
209 | 219 | array( |
— | — | @@ -214,7 +224,7 @@ |
215 | 225 | "eicontfrom - from which point to continue. Use the 'next' value from previous queries.", |
216 | 226 | "Example: query.php?what=embeddedin&titles=Template:Stub&eilimit=10", |
217 | 227 | )), |
218 | | - 'imagelinks' => array( "genPageBackLinksHelper", false, |
| 228 | + 'imagelinks' => array( 'genPageBackLinksHelper', false, |
219 | 229 | array( 'ilfilter', 'illimit', 'ilcontfrom' ), |
220 | 230 | array( array('existing', 'nonredirects', 'all'), 50, null ), |
221 | 231 | array( |
— | — | @@ -225,7 +235,7 @@ |
226 | 236 | "ilcontfrom - from which point to continue. Use the 'next' value from previous queries.", |
227 | 237 | "Example: query.php?what=imagelinks&titles=image:test.jpg&illimit=10", |
228 | 238 | )), |
229 | | - 'revisions' => array( "genPageHistory", false, |
| 239 | + 'revisions' => array( 'genPageHistory', false, |
230 | 240 | array( 'rvcomments', 'rvlimit', 'rvoffset', 'rvstart', 'rvend' ), |
231 | 241 | array( false, 50, 0, null, null ), |
232 | 242 | array( |
— | — | @@ -238,6 +248,10 @@ |
239 | 249 | "rvend - timestamp of the latest entry", |
240 | 250 | "Example: query.php?what=revisions&titles=Main%20Page&rvlimit=10&rvcomments", |
241 | 251 | )), |
| 252 | + 'content' => array( 'genPageContent', false, null, null, array( |
| 253 | + "Raw page content", |
| 254 | + "Example: query.php?what=content&titles=Main%20Page", |
| 255 | + )), |
242 | 256 | ); |
243 | 257 | |
244 | 258 | /** |
— | — | @@ -269,6 +283,9 @@ |
270 | 284 | $this->normalizedTitles = array(); |
271 | 285 | } |
272 | 286 | |
| 287 | + /** |
| 288 | + * The core function - executes meta generators, populates basic page info, and then fills in the required additional data for all pages |
| 289 | + */ |
273 | 290 | function execute() { |
274 | 291 | |
275 | 292 | // Process metadata generators |
— | — | @@ -286,6 +303,9 @@ |
287 | 304 | } |
288 | 305 | } |
289 | 306 | |
| 307 | + /** |
| 308 | + * Helper method to call generators (either meta or non-meta) |
| 309 | + */ |
290 | 310 | function callGenerators( $callMetaGenerators ) { |
291 | 311 | foreach( $this->propGenerators as $property => &$generator ) { |
292 | 312 | if( $generator[GEN_ISMETA] === $callMetaGenerators && in_array( $property, $this->properties )) { |
— | — | @@ -294,6 +314,9 @@ |
295 | 315 | } |
296 | 316 | } |
297 | 317 | |
| 318 | + /** |
| 319 | + * Output the result to the user |
| 320 | + */ |
298 | 321 | function output($isError = false) { |
299 | 322 | global $wgRequest, $wgUser; |
300 | 323 | |
— | — | @@ -352,6 +375,9 @@ |
353 | 376 | } |
354 | 377 | } |
355 | 378 | |
| 379 | + /** |
| 380 | + * Return an array of values that were given in a "a|b|c" notation, after it validates them against the list allowed values. |
| 381 | + */ |
356 | 382 | function parseMultiValue( $valueName, $allowedValues ) { |
357 | 383 | global $wgRequest; |
358 | 384 | |
— | — | @@ -366,9 +392,16 @@ |
367 | 393 | return $valuesList; |
368 | 394 | } |
369 | 395 | |
| 396 | + |
370 | 397 | // |
371 | 398 | // ************************************* GENERATORS ************************************* |
372 | 399 | // |
| 400 | + |
| 401 | + |
| 402 | + /** |
| 403 | + * Creates lists of pages to work on. User parameters 'titles' and 'pageids' will be added to the list, and information from page table will be provided. |
| 404 | + * As the result of this method, $this->redirectPageIds and existingPageIds (arrays) will be available for other generators. |
| 405 | + */ |
373 | 406 | function genPageInfo() { |
374 | 407 | global $wgUser, $wgRequest; |
375 | 408 | |
— | — | @@ -394,7 +427,7 @@ |
395 | 428 | // Make sure we remember the original title that was given to us |
396 | 429 | // This way the caller can correlate new titles with the originally requested if they change namespaces, etc |
397 | 430 | if( $titleString !== $titleObj->getPrefixedText() ) { |
398 | | - $this->normalizedTitles[$titleString] = &$titleObj; |
| 431 | + $this->normalizedTitles[$titleString] = $titleObj; |
399 | 432 | } |
400 | 433 | } |
401 | 434 | if ( $linkBatch->isEmpty() ) { |
— | — | @@ -530,6 +563,30 @@ |
531 | 564 | // When normalized title differs from what was given, append the given title(s) |
532 | 565 | // |
533 | 566 | foreach( $this->normalizedTitles as $givenTitle => &$title ) { |
| 567 | + |
| 568 | + |
| 569 | + |
| 570 | + |
| 571 | + |
| 572 | + |
| 573 | + |
| 574 | + |
| 575 | + |
| 576 | +echo "RAW: $givenTitle => " . $title->getPrefixedText() . "\n<br>"; |
| 577 | + |
| 578 | + |
| 579 | + |
| 580 | + |
| 581 | + |
| 582 | + |
| 583 | + |
| 584 | + |
| 585 | + |
| 586 | + |
| 587 | + |
| 588 | + |
| 589 | + |
| 590 | + |
534 | 591 | $pageId = $this->pageIdByText[$title->getPrefixedText()]; |
535 | 592 | $data = &$this->data['pages'][$pageId]['rawTitles']; |
536 | 593 | $data['_element'] = 'title'; |
— | — | @@ -539,9 +596,15 @@ |
540 | 597 | return true; // success |
541 | 598 | } |
542 | 599 | |
| 600 | + |
543 | 601 | // |
544 | 602 | // ************************************* META GENERATORS ************************************* |
545 | 603 | // |
| 604 | + |
| 605 | + |
| 606 | + /** |
| 607 | + * Get general site information |
| 608 | + */ |
546 | 609 | function genMetaSiteInfo(&$prop, &$genInfo) { |
547 | 610 | global $wgSitename, $wgVersion, $wgCapitalLinks; |
548 | 611 | $meta = array(); |
— | — | @@ -556,6 +619,9 @@ |
557 | 620 | $this->data['meta']['site'] = $meta; |
558 | 621 | } |
559 | 622 | |
| 623 | + /** |
| 624 | + * Get the list of localized namespaces |
| 625 | + */ |
560 | 626 | function genMetaNamespaceInfo(&$prop, &$genInfo) { |
561 | 627 | global $wgContLang; |
562 | 628 | $meta = array(); |
— | — | @@ -566,21 +632,30 @@ |
567 | 633 | $this->data['meta']['namespaces'] = $meta; |
568 | 634 | } |
569 | 635 | |
| 636 | + /** |
| 637 | + * Get current user's status information |
| 638 | + */ |
570 | 639 | function genMetaUserInfo(&$prop, &$genInfo) { |
571 | 640 | global $wgUser; |
572 | 641 | |
| 642 | + extract( $this->getParams( $prop, $genInfo )); |
573 | 643 | $meta = array(); |
574 | 644 | $meta['name'] = $wgUser->getName(); |
575 | 645 | if( $wgUser->isAnon() ) $meta['anonymous'] = ''; |
576 | 646 | if( $wgUser->isBot() ) $meta['bot'] = ''; |
577 | 647 | if( $wgUser->isBlocked() ) $meta[' blocked'] = ''; |
578 | | - $meta['groups'] = $wgUser->getGroups(); |
579 | | - $meta['groups']['_element'] = 'g'; |
580 | | - $meta['rights'] = $wgUser->getRights(); |
581 | | - $meta['rights']['_element'] = 'r'; |
| 648 | + if( $uiextended ) { |
| 649 | + $meta['groups'] = $wgUser->getGroups(); |
| 650 | + $meta['groups']['_element'] = 'g'; |
| 651 | + $meta['rights'] = $wgUser->getRights(); |
| 652 | + $meta['rights']['_element'] = 'r'; |
| 653 | + } |
582 | 654 | $this->data['meta']['user'] = $meta; |
583 | 655 | } |
584 | 656 | |
| 657 | + /** |
| 658 | + * Add pagids of the most recently modified pages to the output |
| 659 | + */ |
585 | 660 | function genMetaRecentChanges(&$prop, &$genInfo) { |
586 | 661 | |
587 | 662 | extract( $this->getParams( $prop, $genInfo )); |
— | — | @@ -636,6 +711,9 @@ |
637 | 712 | $this->db->freeResult( $res ); |
638 | 713 | } |
639 | 714 | |
| 715 | + /** |
| 716 | + * Add user pages to the list of titles to output (the actual user pages might not exist) |
| 717 | + */ |
640 | 718 | function genUserPages(&$prop, &$genInfo) { |
641 | 719 | global $wgContLang; |
642 | 720 | |
— | — | @@ -661,50 +739,52 @@ |
662 | 740 | $this->db->freeResult( $res ); |
663 | 741 | } |
664 | 742 | |
665 | | - // |
666 | | - // TODO: This is very inefficient - we can get the actual page information, instead we make two identical query. |
667 | | - // |
| 743 | + /** |
| 744 | + * Add all pages by a given namespace to the output |
| 745 | + */ |
668 | 746 | function genMetaAllPages(&$prop, &$genInfo) { |
| 747 | + // |
| 748 | + // TODO: This is very inefficient - we can get the actual page information, instead we make two identical query. |
| 749 | + // |
669 | 750 | global $wgContLang; |
670 | 751 | extract( $this->getParams( $prop, $genInfo )); |
671 | 752 | |
672 | 753 | $ns = $wgContLang->getNsText($apnamespace); |
673 | 754 | if( $ns === false ) { |
674 | 755 | $this->dieUsage( "Unknown namespace $ns", 'ap_badnamespace' ); |
| 756 | + } else if( strlen($ns) > 0 ) { |
| 757 | + $ns .= ':'; |
675 | 758 | } |
676 | | - $ns .= ':'; |
677 | 759 | |
678 | 760 | $this->startProfiling(); |
679 | 761 | $res = $this->db->select( |
680 | 762 | 'page', |
681 | 763 | 'page_title', |
682 | | - array( 'page_namespace=' . $this->db->addQuotes($apnamespace) . ' AND page_title>=' . $this->db->addQuotes($apfrom) ), |
| 764 | + array( 'page_namespace' => intval($apnamespace), 'page_title>=' . $this->db->addQuotes($apfrom) ), |
683 | 765 | $this->classname . '::genMetaAllPages', |
684 | | - array( 'FORCE INDEX' => 'name_title', 'LIMIT' => $aplimit+1, 'ORDER BY' => 'page_title' )); |
| 766 | + array( 'FORCE INDEX' => 'name_title', 'LIMIT' => $aplimit+1, 'ORDER BY' => 'page_namespace, page_title' )); |
685 | 767 | $this->endProfiling($prop); |
686 | 768 | |
687 | 769 | // Add found page ids to the list of requested titles - they will be auto-populated later |
688 | 770 | $count = 0; |
689 | 771 | while ( $row = $this->db->fetchObject( $res ) ) { |
690 | | - if( ++$count >= $aplimit ) { |
691 | | - // We've reached the one extra which shows that there are |
692 | | - // additional pages to be had. Stop here... |
| 772 | + if( ++$count > $aplimit ) { |
| 773 | + // We've reached the one extra which shows that there are additional pages to be had. Stop here... |
| 774 | + $this->addStatusMessage( $prop, array('next' => $row->page_title) ); |
693 | 775 | break; |
694 | 776 | } |
695 | 777 | $this->addRaw( 'titles', $ns . $row->page_title ); |
696 | 778 | } |
697 | | - if( $count < $aplimit || !$row ) { |
698 | | - $this->addStatusMessage( $prop, array('next' => 0) ); |
699 | | - } else { |
700 | | - $this->addStatusMessage( $prop, array('next' => $row->page_title) ); |
701 | | - } |
702 | 779 | $this->db->freeResult( $res ); |
703 | 780 | } |
704 | 781 | |
705 | | - // |
706 | | - // TODO: This is very inefficient - we can get the actual page information, instead we make two identical query. |
707 | | - // |
| 782 | + /** |
| 783 | + * Add pages by the namespace without language links to the output |
| 784 | + */ |
708 | 785 | function genMetaNoLangLinksPages(&$prop, &$genInfo) { |
| 786 | + // |
| 787 | + // TODO: This is very inefficient - we can get the actual page information, instead we make two identical query. |
| 788 | + // |
709 | 789 | global $wgContLang; |
710 | 790 | extract( $this->getParams( $prop, $genInfo )); |
711 | 791 | |
— | — | @@ -714,11 +794,11 @@ |
715 | 795 | // Find all pages without any rows in the langlinks table |
716 | 796 | // |
717 | 797 | $sql = 'SELECT' |
718 | | - . ' page_id' |
| 798 | + . ' page_id, page_title' |
719 | 799 | . " FROM $page LEFT JOIN $langlinks ON page_id = ll_from" |
720 | 800 | . ' WHERE' |
721 | | - . ' ll_from IS NULL AND page_id >= ' . intval($nlfromid) |
722 | | - . ' ORDER BY page_id' |
| 801 | + . ' ll_from IS NULL AND page_namespace=' . intval($nlnamespace) . ' AND page_title>=' . $this->db->addQuotes($nlfrom) |
| 802 | + . ' ORDER BY page_namespace, page_title' |
723 | 803 | . ' LIMIT ' . intval($nllimit+1); |
724 | 804 | |
725 | 805 | $this->startProfiling(); |
— | — | @@ -728,20 +808,17 @@ |
729 | 809 | // Add found page ids to the list of requested titles - they will be auto-populated later |
730 | 810 | $count = 0; |
731 | 811 | while ( $row = $this->db->fetchObject( $res ) ) { |
732 | | - if( ++$count >= $nllimit ) { |
733 | | - // We've reached the one extra which shows that there are |
734 | | - // additional pages to be had. Stop here... |
| 812 | + if( ++$count > $nllimit ) { |
| 813 | + // We've reached the one extra which shows that there are additional pages to be had. Stop here... |
| 814 | + $this->addStatusMessage( $prop, array('next' => $row->page_title) ); |
735 | 815 | break; |
736 | 816 | } |
737 | 817 | $this->addRaw( 'pageids', $row->page_id ); |
738 | 818 | } |
739 | | - if( $count < $nllimit || !$row ) { |
740 | | - $this->addStatusMessage( $prop, array('next' => 0) ); |
741 | | - } else { |
742 | | - $this->addStatusMessage( $prop, array('next' => $row->page_id) ); |
743 | | - } |
744 | 819 | $this->db->freeResult( $res ); |
745 | 820 | } |
| 821 | + |
| 822 | + |
746 | 823 | // |
747 | 824 | // ************************************* PAGE INFO GENERATORS ************************************* |
748 | 825 | // |
— | — | @@ -851,7 +928,7 @@ |
852 | 929 | // |
853 | 930 | $parameters = $this->getParams( $prop, $genInfo ); |
854 | 931 | $contFrom = $parameters["{$code}contfrom"]; |
855 | | - $limit = intval($parameters["{$code}limit"]) + 1; |
| 932 | + $limit = intval($parameters["{$code}limit"]); |
856 | 933 | $filter = $parameters["{$code}filter"]; |
857 | 934 | if( count($filter) != 1 ) { |
858 | 935 | $this->dieUsage( "{$code}filter must either be 'all', 'existing', or 'nonredirects'", "{$code}_badmultifilter" ); |
— | — | @@ -947,7 +1024,7 @@ |
948 | 1025 | ."{$prefix}_from >= " . intval($fromPageId) . "))))"; |
949 | 1026 | } |
950 | 1027 | } |
951 | | - $options = array( 'ORDER BY' => $orderBy, 'LIMIT' => $limit ); |
| 1028 | + $options = array( 'ORDER BY' => $orderBy, 'LIMIT' => $limit+1 ); |
952 | 1029 | // |
953 | 1030 | // Execute |
954 | 1031 | // |
— | — | @@ -962,24 +1039,22 @@ |
963 | 1040 | |
964 | 1041 | $count = 0; |
965 | 1042 | while ( $row = $this->db->fetchObject( $res ) ) { |
966 | | - if( ++$count >= $limit ) { |
967 | | - // We've reached the one extra which shows that there are |
968 | | - // additional pages to be had. Stop here... |
| 1043 | + if( ++$count > $limit ) { |
| 1044 | + // We've reached the one extra which shows that there are additional pages to be had. Stop here... |
| 1045 | + $this->addStatusMessage( $prop, |
| 1046 | + array('next' => ($isImage ? NS_IMAGE : $row->to_namespace) ."|{$row->to_title}|{$row->from_id}") ); |
969 | 1047 | break; |
970 | 1048 | } |
971 | 1049 | $pageId = $this->lookupPageIdByTitle( ($isImage ? NS_IMAGE : $row->to_namespace), $row->to_title ); |
972 | 1050 | $values = $this->getLinkInfo( $row->from_namespace, $row->from_title, $row->from_id ); |
973 | 1051 | $this->addPageSubElement( $pageId, $prop, $code, $values ); |
974 | 1052 | } |
975 | | - if( $count < $limit || !$row ) { |
976 | | - $this->addStatusMessage( $prop, array('next' => 0) ); |
977 | | - } else { |
978 | | - $this->addStatusMessage( $prop, |
979 | | - array('next' => ($isImage ? NS_IMAGE : $row->to_namespace) ."|{$row->to_title}|{$row->from_id}") ); |
980 | | - } |
981 | 1053 | $this->db->freeResult( $res ); |
982 | 1054 | } |
983 | 1055 | |
| 1056 | + /** |
| 1057 | + * Add a list of revisions to the page history |
| 1058 | + */ |
984 | 1059 | function genPageHistory(&$prop, &$genInfo) { |
985 | 1060 | if( empty( $this->existingPageIds ) ) { |
986 | 1061 | return; |
— | — | @@ -1032,6 +1107,30 @@ |
1033 | 1108 | $this->endProfiling($prop); |
1034 | 1109 | } |
1035 | 1110 | |
| 1111 | + /** |
| 1112 | + * Add the raw content of the pages |
| 1113 | + */ |
| 1114 | + function genPageContent(&$prop, &$genInfo) { |
| 1115 | + if( empty( $this->existingPageIds ) ) { |
| 1116 | + return; |
| 1117 | + } |
| 1118 | + $this->startProfiling(); |
| 1119 | + $res = $this->db->select( |
| 1120 | + array('page', 'text'), |
| 1121 | + array('page_id', 'old_text'), |
| 1122 | + array('page_latest = old_id', 'page_id' => $this->existingPageIds), |
| 1123 | + $this->classname . '::genPageContent' |
| 1124 | + ); |
| 1125 | + $this->endProfiling($prop); |
| 1126 | + |
| 1127 | + while ( $row = $this->db->fetchObject( $res ) ) { |
| 1128 | + $this->addPageSubElement( $row->page_id, $prop, 'xml:space', 'preserve', false); |
| 1129 | + $this->addPageSubElement( $row->page_id, $prop, '*', $row->old_text, false); |
| 1130 | + } |
| 1131 | + $this->db->freeResult( $res ); |
| 1132 | + } |
| 1133 | + |
| 1134 | + |
1036 | 1135 | // |
1037 | 1136 | // ************************************* UTILITIES ************************************* |
1038 | 1137 | // |
— | — | @@ -1102,6 +1201,9 @@ |
1103 | 1202 | return $val; |
1104 | 1203 | } |
1105 | 1204 | |
| 1205 | + /** |
| 1206 | + * Creates an array describing the properties of a given link |
| 1207 | + */ |
1106 | 1208 | function getLinkInfo( $ns, $title, $id = -1, $isRedirect = false ) { |
1107 | 1209 | return $this->getTitleInfo( Title::makeTitle( $ns, $title ), $id, $isRedirect ); |
1108 | 1210 | } |
— | — | @@ -1132,6 +1234,23 @@ |
1133 | 1235 | return $data; |
1134 | 1236 | } |
1135 | 1237 | |
| 1238 | + /** |
| 1239 | + * Adds a sub element to the page by its id. |
| 1240 | + * Example for $multiItems = true (useful when there are many subelements with the same name, like langlinks or backlinks) |
| 1241 | + * 'pages' => array ( |
| 1242 | + * $pageId => array ( |
| 1243 | + * $mainElem => array ( |
| 1244 | + * '_element' => $itemElem, |
| 1245 | + * 0 => $params |
| 1246 | + * 1 => $params |
| 1247 | + * ..... |
| 1248 | + * Example for $multiItems = false (useful when there are few elements with unique names) |
| 1249 | + * 'pages' => array ( |
| 1250 | + * $pageId => array ( |
| 1251 | + * $mainElem => array ( |
| 1252 | + * $itemElem => $params |
| 1253 | + * ..... |
| 1254 | + */ |
1136 | 1255 | function addPageSubElement( $pageId, $mainElem, $itemElem, $params, $multiItems = true ) { |
1137 | 1256 | $data = & $this->data['pages'][$pageId][$mainElem]; |
1138 | 1257 | if( $multiItems ) { |
— | — | @@ -1145,6 +1264,9 @@ |
1146 | 1265 | } |
1147 | 1266 | } |
1148 | 1267 | |
| 1268 | + /** |
| 1269 | + * Validate the proper format of the timestamp string (14 digits), and add quotes to it. |
| 1270 | + */ |
1149 | 1271 | function prepareTimestamp( $value ) { |
1150 | 1272 | if ( preg_match( '/^[0-9]{14}$/', $value ) ) { |
1151 | 1273 | return $this->db->addQuotes( $value ); |
— | — | @@ -1166,25 +1288,25 @@ |
1167 | 1289 | |
1168 | 1290 | $indentSize = 12; |
1169 | 1291 | $indstr = str_repeat(" ", $indentSize+7); |
| 1292 | + $formatString = " %-{$indentSize}s - %s\n\n"; |
1170 | 1293 | |
1171 | 1294 | $formats = ""; |
1172 | 1295 | foreach( $this->outputGenerators as $format => &$generator ) { |
1173 | | - $formats .= sprintf( " %-{$indentSize}s - %s\n", |
1174 | | - $format, |
| 1296 | + $formats .= sprintf( $formatString, $format, |
1175 | 1297 | mergeDescriptionStrings($generator[GEN_DESC], $indstr)); |
1176 | 1298 | } |
1177 | 1299 | |
1178 | 1300 | $props = "\n *These properties apply to the entire site*\n"; |
1179 | 1301 | foreach( $this->propGenerators as $property => &$generator ) { |
1180 | 1302 | if( $generator[GEN_ISMETA] ) { |
1181 | | - $props .= sprintf( " %-{$indentSize}s - %s\n", $property, |
| 1303 | + $props .= sprintf( $formatString, $property, |
1182 | 1304 | mergeDescriptionStrings($generator[GEN_DESC], $indstr)); |
1183 | 1305 | } |
1184 | 1306 | } |
1185 | 1307 | $props .= "\n *These properties apply to the specified pages*\n"; |
1186 | 1308 | foreach( $this->propGenerators as $property => &$generator ) { |
1187 | 1309 | if( !$generator[GEN_ISMETA] ) { |
1188 | | - $props .= sprintf( " %-{$indentSize}s - %s\n", $property, |
| 1310 | + $props .= sprintf( $formatString, $property, |
1189 | 1311 | mergeDescriptionStrings($generator[GEN_DESC], $indstr)); |
1190 | 1312 | } |
1191 | 1313 | } |
— | — | @@ -1195,49 +1317,50 @@ |
1196 | 1318 | "", |
1197 | 1319 | "*------ Error: $message ($errorcode) ------*", |
1198 | 1320 | "", |
1199 | | - "Summary:", |
| 1321 | + "*Summary*", |
1200 | 1322 | " This API provides a way for your applications to query data directly from the MediaWiki servers.", |
1201 | 1323 | " One or more pieces of information about the site and/or a given list of pages can be retrieved.", |
1202 | 1324 | " Information may be returned in either a machine (xml, json, php) or a human readable (html, dbg) format.", |
1203 | 1325 | "", |
1204 | | - "Usage:", |
| 1326 | + "*Usage*", |
1205 | 1327 | " query.php ? format=... & what=...|...|... & titles=...|...|... & ...", |
1206 | 1328 | "", |
1207 | | - "Common parameters:", |
| 1329 | + "*Common parameters*", |
1208 | 1330 | " format - How should the output be formatted. See formats section.", |
1209 | 1331 | " what - What information the server should return. See properties section.", |
1210 | 1332 | " titles - A list of titles, separated by the pipe '|' symbol.", |
1211 | 1333 | " pageids - A list of page ids, separated by the pipe '|' symbol.", |
1212 | 1334 | " noprofile - When present, each sql query execution time will be hidden. (Optional)", |
1213 | 1335 | "", |
1214 | | - "Examples:", |
| 1336 | + "*Examples*", |
1215 | 1337 | " query.php?format=xml&what=links|templates&titles=User:Yurik", |
1216 | 1338 | " This query will return a list of all links and templates used on the User:Yurik", |
1217 | 1339 | "", |
1218 | 1340 | " query.php?format=xml&what=revisions&titles=Main_Page&rvlimit=100&rvstart=20060401000000&rvcomments", |
1219 | 1341 | " Get a list of 100 last revisions of the main page with comments, but only if it happened after midnight April 1st 2006", |
1220 | 1342 | "", |
1221 | | - "Supported Formats:", |
| 1343 | + "", |
| 1344 | + "*Supported Formats*", |
1222 | 1345 | $formats, |
1223 | 1346 | "", |
1224 | | - "Supported Properties:", |
| 1347 | + "*Supported Properties*", |
1225 | 1348 | $props, |
1226 | 1349 | "", |
1227 | | - "Notes:", |
| 1350 | + "*Notes*", |
1228 | 1351 | " Some properties may add status information to the 'query' element.", |
1229 | 1352 | "", |
1230 | | - "Credits:", |
| 1353 | + "*Credits*", |
| 1354 | + " This feature is maintained by Yuri Astrakhan (FirstnameLastname@gmail.com)", |
| 1355 | + " You can also leave your comments and suggestions at http://en.wikipedia.org/wiki/User_talk:Yurik", |
| 1356 | + "", |
1231 | 1357 | " This extension came as the result of IRC discussion between Yuri Astrakhan (en:Yurik), Tim Starling (en:Tim Starling), and Daniel Kinzler(de:Duesentrieb)", |
1232 | 1358 | " The extension was first implemented by Tim to provide interlanguage links and history.", |
1233 | 1359 | " It was later completelly rewritten by Yuri to allow for modular properties, meta information, and various formatting options.", |
1234 | 1360 | "", |
1235 | | - " The code is maintained by Yuri Astrakhan (FirstnameLastname@gmail.com)", |
1236 | | - " You can also leave your comments and suggestions at http://en.wikipedia.org/wiki/User_talk:Yurik", |
1237 | | - "", |
1238 | | - "User Status:", |
| 1361 | + "*User Status*", |
1239 | 1362 | " You are " . ($wgUser->isAnon() ? "an anonymous" : "a logged-in") . " " . ($wgUser->isBot() ? "bot" : "user") . " " . $wgUser->getName(), |
1240 | 1363 | "", |
1241 | | - "Version:", |
| 1364 | + "*Version*", |
1242 | 1365 | ' $Id$', |
1243 | 1366 | "", |
1244 | 1367 | ); |
— | — | @@ -1248,6 +1371,9 @@ |
1249 | 1372 | die(0); |
1250 | 1373 | } |
1251 | 1374 | |
| 1375 | + /** |
| 1376 | + * Adds a status message into the <query> element, for a given module. |
| 1377 | + */ |
1252 | 1378 | function addStatusMessage( $module, $value, $preserveXmlSpacing = false ) { |
1253 | 1379 | if( !array_key_exists( 'query', $this->data )) { |
1254 | 1380 | $this->data['query'] = array(); |
— | — | @@ -1274,15 +1400,25 @@ |
1275 | 1401 | } |
1276 | 1402 | } |
1277 | 1403 | |
| 1404 | + /** |
| 1405 | + * Records the time of the call to this method |
| 1406 | + */ |
1278 | 1407 | function startProfiling() { |
1279 | 1408 | $this->startTime = wfTime(); |
1280 | 1409 | } |
| 1410 | + |
| 1411 | + /** |
| 1412 | + * Records the running time of the given module since last startProfiling() call. |
| 1413 | + */ |
1281 | 1414 | function endProfiling( $module ) { |
1282 | 1415 | $timeDelta = wfTime() - $this->startTime; |
1283 | 1416 | unset($this->startTime); |
1284 | 1417 | $this->addStatusMessage( $module, array( 'time' => sprintf( "%1.2fms", $timeDelta * 1000.0 ) )); |
1285 | 1418 | } |
1286 | 1419 | |
| 1420 | + /** |
| 1421 | + * Validate the value against the minimum and user/bot maximum limits. Prints usage info on failure. |
| 1422 | + */ |
1287 | 1423 | function validateLimit( $varname, &$value, $max, $botMax = false, $min = 1 ) { |
1288 | 1424 | global $wgUser; |
1289 | 1425 | if( !$botMax ) $botMax = $max; |
— | — | @@ -1302,9 +1438,14 @@ |
1303 | 1439 | } |
1304 | 1440 | } |
1305 | 1441 | |
| 1442 | + |
1306 | 1443 | // |
1307 | 1444 | // ************************************* Print Methods ************************************* |
1308 | 1445 | // |
| 1446 | + |
| 1447 | +/** |
| 1448 | +* Prints data in html format. Escapes all unsafe characters. Adds an HTML warning in the begining. |
| 1449 | +*/ |
1309 | 1450 | function printHTML( &$data ) { |
1310 | 1451 | global $wgRequest; |
1311 | 1452 | ?> |
— | — | @@ -1330,6 +1471,10 @@ |
1331 | 1472 | </body> |
1332 | 1473 | <?php |
1333 | 1474 | } |
| 1475 | + |
| 1476 | +/** |
| 1477 | +* Prety-print various elements in HTML format, such as xml tags and URLs. This method also replaces any "<" with < |
| 1478 | +*/ |
1334 | 1479 | function htmlprinter( $text ) { |
1335 | 1480 | // encode all tags as safe blue strings |
1336 | 1481 | $text = ereg_replace( '\<([^>]+)\>', '<font color=blue><\1></font>', $text ); |
— | — | @@ -1342,26 +1487,48 @@ |
1343 | 1488 | echo $text; |
1344 | 1489 | } |
1345 | 1490 | |
| 1491 | +/** |
| 1492 | +* Output data in XML format |
| 1493 | +*/ |
1346 | 1494 | function printXML( &$data ) { |
1347 | 1495 | global $wgRequest; |
1348 | 1496 | echo '<?xml version="1.0" encoding="utf-8"?>'; |
1349 | 1497 | recXmlPrint( 'echoprinter', 'yurik', $data, $wgRequest->getCheck('xmlindent') ? -2 : null ); |
1350 | 1498 | } |
| 1499 | +/** |
| 1500 | +* Pass-through printer. |
| 1501 | +*/ |
1351 | 1502 | function echoprinter( $text ) { |
1352 | 1503 | echo $text; |
1353 | 1504 | } |
| 1505 | + |
| 1506 | +/** |
| 1507 | +* Sanitizes the data and prints it with the print_r() |
| 1508 | +*/ |
1354 | 1509 | function printHumanReadable( &$data ) { |
1355 | 1510 | sanitizeOutputData($data); |
1356 | 1511 | print_r($data); |
1357 | 1512 | } |
1358 | | -function printParsableCode( &$data ) { |
1359 | | - sanitizeOutputData($data); |
| 1513 | + |
| 1514 | +/** |
| 1515 | +* Prints the data as is, using var_export(). |
| 1516 | +* This format exposes all internals of the data object unescaped, thus it must never be outputed with meta set to text/* |
| 1517 | +*/ |
| 1518 | +function printDebugCode( &$data ) { |
1360 | 1519 | var_export($data); |
1361 | 1520 | } |
| 1521 | + |
| 1522 | +/** |
| 1523 | +* Sanitizes the data and serialize() it so that other php scripts can easily consume the data |
| 1524 | +*/ |
1362 | 1525 | function printPHP( &$data ) { |
1363 | 1526 | sanitizeOutputData($data); |
1364 | 1527 | echo serialize($data); |
1365 | 1528 | } |
| 1529 | + |
| 1530 | +/** |
| 1531 | +* Sanitizes the data and serializes it in JSON format |
| 1532 | +*/ |
1366 | 1533 | function printJSON( &$data ) { |
1367 | 1534 | sanitizeOutputData($data); |
1368 | 1535 | if ( !function_exists( 'json_encode' ) ) { |
— | — | @@ -1446,6 +1613,9 @@ |
1447 | 1614 | } |
1448 | 1615 | } |
1449 | 1616 | |
| 1617 | +/** |
| 1618 | +* Helper method that merges an array of strings and prepends each line with an indentation string |
| 1619 | +*/ |
1450 | 1620 | function mergeDescriptionStrings( &$value, $indstr ) { |
1451 | 1621 | if( is_array($value) ) { |
1452 | 1622 | $value = implode( "\n", $value ); |
— | — | @@ -1453,6 +1623,9 @@ |
1454 | 1624 | return str_replace("\n", "\n$indstr", $value); |
1455 | 1625 | } |
1456 | 1626 | |
| 1627 | +/** |
| 1628 | +* Merge all known generator parameters into one array of values. Used for logging. |
| 1629 | +*/ |
1457 | 1630 | function mergeParameters( &$generators ) { |
1458 | 1631 | $params = array(); |
1459 | 1632 | foreach( $generators as $property => &$generator ) { |