]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/specials/SpecialAllpages.php
MediaWiki 1.16.1-scripts
[autoinstallsdev/mediawiki.git] / includes / specials / SpecialAllpages.php
1 <?php
2
3 /**
4  * Implements Special:Allpages
5  * @ingroup SpecialPage
6  */
7 class SpecialAllpages extends IncludableSpecialPage {
8
9         /**
10          * Maximum number of pages to show on single subpage.
11          */
12         protected $maxPerPage = 345;
13
14         /**
15          * Maximum number of pages to show on single index subpage.
16          */
17         protected $maxLineCount = 100;
18
19         /**
20          * Maximum number of chars to show for an entry.
21          */
22         protected $maxPageLength = 70;
23
24         /**
25          * Determines, which message describes the input field 'nsfrom'.
26          */
27         protected $nsfromMsg = 'allpagesfrom';
28
29         function __construct( $name = 'Allpages' ){
30                 parent::__construct( $name );
31         }
32
33         /**
34          * Entry point : initialise variables and call subfunctions.
35          * @param $par String: becomes "FOO" when called like Special:Allpages/FOO (default NULL)
36          * @param $specialPage See the SpecialPage object.
37          */
38         function execute( $par ) {
39                 global $wgRequest, $wgOut, $wgContLang;
40
41                 $this->setHeaders();
42                 $this->outputHeader();
43                 $wgOut->allowClickjacking();
44
45                 # GET values
46                 $from = $wgRequest->getVal( 'from', null );
47                 $to = $wgRequest->getVal( 'to', null );
48                 $namespace = $wgRequest->getInt( 'namespace' );
49
50                 $namespaces = $wgContLang->getNamespaces();
51
52                 $wgOut->setPagetitle( 
53                         ( $namespace > 0 && in_array( $namespace, array_keys( $namespaces) ) ) ?
54                         wfMsg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) ) :
55                         wfMsg( 'allarticles' )
56                 );
57
58                 if( isset($par) ) {
59                         $this->showChunk( $namespace, $par, $to );
60                 } elseif( isset($from) && !isset($to) ) {
61                         $this->showChunk( $namespace, $from, $to );
62                 } else {
63                         $this->showToplevel( $namespace, $from, $to );
64                 }
65         }
66
67         /**
68          * HTML for the top form
69          * @param integer $namespace A namespace constant (default NS_MAIN).
70          * @param string $from dbKey we are starting listing at.
71          * @param string $to dbKey we are ending listing at.
72          */
73         function namespaceForm( $namespace = NS_MAIN, $from = '', $to = '' ) {
74                 global $wgScript;
75                 $t = $this->getTitle();
76         
77                 $out  = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) );
78                 $out .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
79                 $out .= Xml::hidden( 'title', $t->getPrefixedText() );
80                 $out .= Xml::openElement( 'fieldset' );
81                 $out .= Xml::element( 'legend', null, wfMsg( 'allpages' ) );
82                 $out .= Xml::openElement( 'table', array( 'id' => 'nsselect', 'class' => 'allpages' ) );
83                 $out .= "<tr>
84         <td class='mw-label'>" .
85                         Xml::label( wfMsg( 'allpagesfrom' ), 'nsfrom' ) .
86                         "       </td>
87         <td class='mw-input'>" .
88                         Xml::input( 'from', 30, str_replace('_',' ',$from), array( 'id' => 'nsfrom' ) ) .
89                         "       </td>
90 </tr>
91 <tr>
92         <td class='mw-label'>" .
93                         Xml::label( wfMsg( 'allpagesto' ), 'nsto' ) .
94                         "       </td>
95                         <td class='mw-input'>" .
96                         Xml::input( 'to', 30, str_replace('_',' ',$to), array( 'id' => 'nsto' ) ) .
97                         "               </td>
98 </tr>
99 <tr>
100         <td class='mw-label'>" .
101                         Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
102                         "       </td>
103                         <td class='mw-input'>" .
104                         Xml::namespaceSelector( $namespace, null ) . ' ' .
105                         Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
106                         "       </td>
107 </tr>";
108                 $out .= Xml::closeElement( 'table' );
109                 $out .= Xml::closeElement( 'fieldset' );
110                 $out .= Xml::closeElement( 'form' );
111                 $out .= Xml::closeElement( 'div' );
112                 return $out;
113         }
114
115         /**
116          * @param integer $namespace (default NS_MAIN)
117          */
118         function showToplevel( $namespace = NS_MAIN, $from = '', $to = '' ) {
119                 global $wgOut;
120
121                 # TODO: Either make this *much* faster or cache the title index points
122                 # in the querycache table.
123
124                 $dbr = wfGetDB( DB_SLAVE );
125                 $out = "";
126                 $where = array( 'page_namespace' => $namespace );
127
128                 $from = Title::makeTitleSafe( $namespace, $from );
129                 $to = Title::makeTitleSafe( $namespace, $to );
130                 $from = ( $from && $from->isLocal() ) ? $from->getDBkey() : null;
131                 $to = ( $to && $to->isLocal() ) ? $to->getDBkey() : null;
132
133                 if( isset($from) )
134                         $where[] = 'page_title >= '.$dbr->addQuotes( $from );
135                 if( isset($to) )
136                         $where[] = 'page_title <= '.$dbr->addQuotes( $to );
137
138                 global $wgMemc;
139                 $key = wfMemcKey( 'allpages', 'ns', $namespace, $from, $to );
140                 $lines = $wgMemc->get( $key );
141
142                 $count = $dbr->estimateRowCount( 'page', '*', $where, __METHOD__ );
143                 $maxPerSubpage = intval($count/$this->maxLineCount);
144                 $maxPerSubpage = max($maxPerSubpage,$this->maxPerPage);
145
146                 if( !is_array( $lines ) ) {
147                         $options = array( 'LIMIT' => 1 );
148                         $options['ORDER BY'] = 'page_title ASC';
149                         $firstTitle = $dbr->selectField( 'page', 'page_title', $where, __METHOD__, $options );
150                         $lastTitle = $firstTitle;
151                         # This array is going to hold the page_titles in order.
152                         $lines = array( $firstTitle );
153                         # If we are going to show n rows, we need n+1 queries to find the relevant titles.
154                         $done = false;
155                         while( !$done ) {
156                                 // Fetch the last title of this chunk and the first of the next
157                                 $chunk = ( $lastTitle === false )
158                                         ? array()
159                                         : array( 'page_title >= ' . $dbr->addQuotes( $lastTitle ) );
160                                 $res = $dbr->select( 'page', /* FROM */
161                                         'page_title', /* WHAT */
162                                         array_merge($where,$chunk),
163                                         __METHOD__,
164                                         array ('LIMIT' => 2, 'OFFSET' => $maxPerSubpage - 1, 'ORDER BY' => 'page_title ASC')
165                                 );
166
167                                 if( $s = $dbr->fetchObject( $res ) ) {
168                                         array_push( $lines, $s->page_title );
169                                 } else {
170                                         // Final chunk, but ended prematurely. Go back and find the end.
171                                         $endTitle = $dbr->selectField( 'page', 'MAX(page_title)',
172                                                 array_merge($where,$chunk),
173                                                 __METHOD__ );
174                                         array_push( $lines, $endTitle );
175                                         $done = true;
176                                 }
177                                 if( $s = $res->fetchObject() ) {
178                                         array_push( $lines, $s->page_title );
179                                         $lastTitle = $s->page_title;
180                                 } else {
181                                         // This was a final chunk and ended exactly at the limit.
182                                         // Rare but convenient!
183                                         $done = true;
184                                 }
185                                 $res->free();
186                         }
187                         $wgMemc->add( $key, $lines, 3600 );
188                 }
189
190                 // If there are only two or less sections, don't even display them.
191                 // Instead, display the first section directly.
192                 if( count( $lines ) <= 2 ) {
193                         if( !empty($lines) ) {
194                                 $this->showChunk( $namespace, $from, $to );
195                         } else {
196                                 $wgOut->addHTML( $this->namespaceForm( $namespace, $from, $to ) );
197                         }
198                         return;
199                 }
200
201                 # At this point, $lines should contain an even number of elements.
202                 $out .= Xml::openElement( 'table', array( 'class' => 'allpageslist' ) );
203                 while( count ( $lines ) > 0 ) {
204                         $inpoint = array_shift( $lines );
205                         $outpoint = array_shift( $lines );
206                         $out .= $this->showline( $inpoint, $outpoint, $namespace );
207                 }
208                 $out .= Xml::closeElement( 'table' );
209                 $nsForm = $this->namespaceForm( $namespace, $from, $to );
210
211                 # Is there more?
212                 if( $this->including() ) {
213                         $out2 = '';
214                 } else {
215                         if( isset($from) || isset($to) ) {
216                                 global $wgUser;
217                                 $out2 = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-form' ) ).
218                                                 '<tr>
219                                                         <td>' .
220                                                                 $nsForm .
221                                                         '</td>
222                                                         <td class="mw-allpages-nav">' .
223                                                                 $wgUser->getSkin()->link( $this->getTitle(), wfMsgHtml ( 'allpages' ),
224                                                                         array(), array(), 'known' ) .
225                                                         "</td>
226                                                 </tr>" .
227                                         Xml::closeElement( 'table' );
228                         } else {
229                                 $out2 = $nsForm;
230                         }
231                 }
232                 $wgOut->addHTML( $out2 . $out );
233         }
234
235         /**
236          * Show a line of "ABC to DEF" ranges of articles
237          * @param string $inpoint Lower limit of pagenames
238          * @param string $outpout Upper limit of pagenames
239          * @param integer $namespace (Default NS_MAIN)
240          */
241         function showline( $inpoint, $outpoint, $namespace = NS_MAIN ) {
242                 global $wgContLang;
243                 $inpointf = htmlspecialchars( str_replace( '_', ' ', $inpoint ) );
244                 $outpointf = htmlspecialchars( str_replace( '_', ' ', $outpoint ) );
245                 // Don't let the length runaway
246                 $inpointf = $wgContLang->truncate( $inpointf, $this->maxPageLength );
247                 $outpointf = $wgContLang->truncate( $outpointf, $this->maxPageLength );
248
249                 $queryparams = $namespace ? "namespace=$namespace&" : '';
250                 $special = $this->getTitle();
251                 $link = $special->escapeLocalUrl( $queryparams . 'from=' . urlencode($inpoint) . '&to=' . urlencode($outpoint) );
252
253                 $out = wfMsgHtml( 'alphaindexline',
254                         "<a href=\"$link\">$inpointf</a></td><td>",
255                         "</td><td><a href=\"$link\">$outpointf</a>"
256                 );
257                 return '<tr><td class="mw-allpages-alphaindexline">' . $out . '</td></tr>';
258         }
259
260         /**
261          * @param integer $namespace (Default NS_MAIN)
262          * @param string $from list all pages from this name (default FALSE)
263          * @param string $to list all pages to this name (default FALSE)
264          */
265         function showChunk( $namespace = NS_MAIN, $from = false, $to = false ) {
266                 global $wgOut, $wgUser, $wgContLang, $wgLang;
267
268                 $sk = $wgUser->getSkin();
269
270                 $fromList = $this->getNamespaceKeyAndText($namespace, $from);
271                 $toList = $this->getNamespaceKeyAndText( $namespace, $to );
272                 $namespaces = $wgContLang->getNamespaces();
273                 $n = 0;
274
275                 if ( !$fromList || !$toList ) {
276                         $out = wfMsgWikiHtml( 'allpagesbadtitle' );
277                 } elseif ( !in_array( $namespace, array_keys( $namespaces ) ) ) {
278                         // Show errormessage and reset to NS_MAIN
279                         $out = wfMsgExt( 'allpages-bad-ns', array( 'parseinline' ), $namespace );
280                         $namespace = NS_MAIN;
281                 } else {
282                         list( $namespace, $fromKey, $from ) = $fromList;
283                         list( $namespace2, $toKey, $to ) = $toList;
284
285                         $dbr = wfGetDB( DB_SLAVE );
286                         $conds = array(
287                                 'page_namespace' => $namespace,
288                                 'page_title >= ' . $dbr->addQuotes( $fromKey )
289                         );
290                         if( $toKey !== "" ) {
291                                 $conds[] = 'page_title <= ' . $dbr->addQuotes( $toKey );
292                         }
293
294                         $res = $dbr->select( 'page',
295                                 array( 'page_namespace', 'page_title', 'page_is_redirect' ),
296                                 $conds,
297                                 __METHOD__,
298                                 array(
299                                         'ORDER BY'  => 'page_title',
300                                         'LIMIT'     => $this->maxPerPage + 1,
301                                         'USE INDEX' => 'name_title',
302                                 )
303                         );
304
305                         if( $res->numRows() > 0 ) {
306                                 $out = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-chunk' ) );
307                                 while( ( $n < $this->maxPerPage ) && ( $s = $res->fetchObject() ) ) {
308                                         $t = Title::makeTitle( $s->page_namespace, $s->page_title );
309                                         if( $t ) {
310                                                 $link = ( $s->page_is_redirect ? '<div class="allpagesredirect">' : '' ) .
311                                                         $sk->linkKnown( $t, htmlspecialchars( $t->getText() ) ) .
312                                                         ($s->page_is_redirect ? '</div>' : '' );
313                                         } else {
314                                                 $link = '[[' . htmlspecialchars( $s->page_title ) . ']]';
315                                         }
316                                         if( $n % 3 == 0 ) {
317                                                 $out .= '<tr>';
318                                         }
319                                         $out .= "<td width=\"33%\">$link</td>";
320                                         $n++;
321                                         if( $n % 3 == 0 ) {
322                                                 $out .= "</tr>\n";
323                                         }
324                                 }
325                                 if( ($n % 3) != 0 ) {
326                                         $out .= "</tr>\n";
327                                 }
328                                 $out .= Xml::closeElement( 'table' );
329                         } else {
330                                 $out = '';
331                         }
332                 }
333
334                 if ( $this->including() ) {
335                         $out2 = '';
336                 } else {
337                         if( $from == '' ) {
338                                 // First chunk; no previous link.
339                                 $prevTitle = null;
340                         } else {
341                                 # Get the last title from previous chunk
342                                 $dbr = wfGetDB( DB_SLAVE );
343                                 $res_prev = $dbr->select(
344                                         'page',
345                                         'page_title',
346                                         array( 'page_namespace' => $namespace, 'page_title < '.$dbr->addQuotes($from) ),
347                                         __METHOD__,
348                                         array( 'ORDER BY' => 'page_title DESC', 
349                                                 'LIMIT' => $this->maxPerPage, 'OFFSET' => ($this->maxPerPage - 1 )
350                                         )
351                                 );
352
353                                 # Get first title of previous complete chunk
354                                 if( $dbr->numrows( $res_prev ) >= $this->maxPerPage ) {
355                                         $pt = $dbr->fetchObject( $res_prev );
356                                         $prevTitle = Title::makeTitle( $namespace, $pt->page_title );
357                                 } else {
358                                         # The previous chunk is not complete, need to link to the very first title
359                                         # available in the database
360                                         $options = array( 'LIMIT' => 1 );
361                                         if ( ! $dbr->implicitOrderby() ) {
362                                                 $options['ORDER BY'] = 'page_title';
363                                         }
364                                         $reallyFirstPage_title = $dbr->selectField( 'page', 'page_title',
365                                                 array( 'page_namespace' => $namespace ), __METHOD__, $options );
366                                         # Show the previous link if it s not the current requested chunk
367                                         if( $from != $reallyFirstPage_title ) {
368                                                 $prevTitle =  Title::makeTitle( $namespace, $reallyFirstPage_title );
369                                         } else {
370                                                 $prevTitle = null;
371                                         }
372                                 }
373                         }
374
375                         $self = $this->getTitle();
376
377                         $nsForm = $this->namespaceForm( $namespace, $from, $to );
378                         $out2 = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-form' ) ).
379                                                 '<tr>
380                                                         <td>' .
381                                                                 $nsForm .
382                                                         '</td>
383                                                         <td class="mw-allpages-nav">' .
384                                                                 $sk->link( $self, wfMsgHtml ( 'allpages' ), array(), array(), 'known' );
385
386                         # Do we put a previous link ?
387                         if( isset( $prevTitle ) &&  $pt = $prevTitle->getText() ) {
388                                 $query = array( 'from' => $prevTitle->getText() );
389
390                                 if( $namespace )
391                                         $query['namespace'] = $namespace;
392
393                                 $prevLink = $sk->linkKnown(
394                                         $self,
395                                         htmlspecialchars( wfMsg( 'prevpage', $pt ) ),
396                                         array(),
397                                         $query
398                                 );
399                                 $out2 = $wgLang->pipeList( array( $out2, $prevLink ) );
400                         }
401
402                         if( $n == $this->maxPerPage && $s = $res->fetchObject() ) {
403                                 # $s is the first link of the next chunk
404                                 $t = Title::MakeTitle($namespace, $s->page_title);
405                                 $query = array( 'from' => $t->getText() );
406
407                                 if( $namespace )
408                                         $query['namespace'] = $namespace;
409
410                                 $nextLink = $sk->linkKnown(
411                                         $self,
412                                         htmlspecialchars( wfMsg( 'nextpage', $t->getText() ) ),
413                                         array(),
414                                         $query
415                                 );
416                                 $out2 = $wgLang->pipeList( array( $out2, $nextLink ) );
417                         }
418                         $out2 .= "</td></tr></table>";
419                 }
420
421                 $wgOut->addHTML( $out2 . $out );
422                 if( isset($prevLink) or isset($nextLink) ) {
423                         $wgOut->addHTML( '<hr /><p class="mw-allpages-nav">' );
424                         if( isset( $prevLink ) ) {
425                                 $wgOut->addHTML( $prevLink );
426                         }
427                         if( isset( $prevLink ) && isset( $nextLink ) ) {
428                                 $wgOut->addHTML( wfMsgExt( 'pipe-separator' , 'escapenoentities' ) );
429                         }
430                         if( isset( $nextLink ) ) {
431                                 $wgOut->addHTML( $nextLink );
432                         }
433                         $wgOut->addHTML( '</p>' );
434
435                 }
436
437         }
438
439         /**
440          * @param int $ns the namespace of the article
441          * @param string $text the name of the article
442          * @return array( int namespace, string dbkey, string pagename ) or NULL on error
443          * @static (sort of)
444          * @access private
445          */
446         function getNamespaceKeyAndText($ns, $text) {
447                 if ( $text == '' )
448                         return array( $ns, '', '' ); # shortcut for common case
449
450                 $t = Title::makeTitleSafe($ns, $text);
451                 if ( $t && $t->isLocal() ) {
452                         return array( $t->getNamespace(), $t->getDBkey(), $t->getText() );
453                 } else if ( $t ) {
454                         return null;
455                 }
456
457                 # try again, in case the problem was an empty pagename
458                 $text = preg_replace('/(#|$)/', 'X$1', $text);
459                 $t = Title::makeTitleSafe($ns, $text);
460                 if ( $t && $t->isLocal() ) {
461                         return array( $t->getNamespace(), '', '' );
462                 } else {
463                         return null;
464                 }
465         }
466 }