00001 <?php
00002
00007 class SpecialAllpages extends IncludableSpecialPage {
00008
00012 protected $maxPerPage = 345;
00013
00017 protected $maxLineCount = 100;
00018
00022 protected $maxPageLength = 70;
00023
00027 protected $nsfromMsg = 'allpagesfrom';
00028
00029 function __construct( $name = 'Allpages' ){
00030 parent::__construct( $name );
00031 }
00032
00038 function execute( $par ) {
00039 global $wgRequest, $wgOut, $wgContLang;
00040
00041 $this->setHeaders();
00042 $this->outputHeader();
00043 $wgOut->allowClickjacking();
00044
00045 # GET values
00046 $from = $wgRequest->getVal( 'from', null );
00047 $to = $wgRequest->getVal( 'to', null );
00048 $namespace = $wgRequest->getInt( 'namespace' );
00049
00050 $namespaces = $wgContLang->getNamespaces();
00051
00052 $wgOut->setPagetitle(
00053 ( $namespace > 0 && in_array( $namespace, array_keys( $namespaces) ) ) ?
00054 wfMsg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) ) :
00055 wfMsg( 'allarticles' )
00056 );
00057
00058 if( isset($par) ) {
00059 $this->showChunk( $namespace, $par, $to );
00060 } elseif( isset($from) && !isset($to) ) {
00061 $this->showChunk( $namespace, $from, $to );
00062 } else {
00063 $this->showToplevel( $namespace, $from, $to );
00064 }
00065 }
00066
00073 function namespaceForm( $namespace = NS_MAIN, $from = '', $to = '' ) {
00074 global $wgScript;
00075 $t = $this->getTitle();
00076
00077 $out = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) );
00078 $out .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
00079 $out .= Xml::hidden( 'title', $t->getPrefixedText() );
00080 $out .= Xml::openElement( 'fieldset' );
00081 $out .= Xml::element( 'legend', null, wfMsg( 'allpages' ) );
00082 $out .= Xml::openElement( 'table', array( 'id' => 'nsselect', 'class' => 'allpages' ) );
00083 $out .= "<tr>
00084 <td class='mw-label'>" .
00085 Xml::label( wfMsg( 'allpagesfrom' ), 'nsfrom' ) .
00086 " </td>
00087 <td class='mw-input'>" .
00088 Xml::input( 'from', 30, str_replace('_',' ',$from), array( 'id' => 'nsfrom' ) ) .
00089 " </td>
00090 </tr>
00091 <tr>
00092 <td class='mw-label'>" .
00093 Xml::label( wfMsg( 'allpagesto' ), 'nsto' ) .
00094 " </td>
00095 <td class='mw-input'>" .
00096 Xml::input( 'to', 30, str_replace('_',' ',$to), array( 'id' => 'nsto' ) ) .
00097 " </td>
00098 </tr>
00099 <tr>
00100 <td class='mw-label'>" .
00101 Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
00102 " </td>
00103 <td class='mw-input'>" .
00104 Xml::namespaceSelector( $namespace, null ) . ' ' .
00105 Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
00106 " </td>
00107 </tr>";
00108 $out .= Xml::closeElement( 'table' );
00109 $out .= Xml::closeElement( 'fieldset' );
00110 $out .= Xml::closeElement( 'form' );
00111 $out .= Xml::closeElement( 'div' );
00112 return $out;
00113 }
00114
00118 function showToplevel( $namespace = NS_MAIN, $from = '', $to = '' ) {
00119 global $wgOut;
00120
00121 # TODO: Either make this *much* faster or cache the title index points
00122 # in the querycache table.
00123
00124 $dbr = wfGetDB( DB_SLAVE );
00125 $out = "";
00126 $where = array( 'page_namespace' => $namespace );
00127
00128 $from = Title::makeTitleSafe( $namespace, $from );
00129 $to = Title::makeTitleSafe( $namespace, $to );
00130 $from = ( $from && $from->isLocal() ) ? $from->getDBkey() : null;
00131 $to = ( $to && $to->isLocal() ) ? $to->getDBkey() : null;
00132
00133 if( isset($from) )
00134 $where[] = 'page_title >= '.$dbr->addQuotes( $from );
00135 if( isset($to) )
00136 $where[] = 'page_title <= '.$dbr->addQuotes( $to );
00137
00138 global $wgMemc;
00139 $key = wfMemcKey( 'allpages', 'ns', $namespace, $from, $to );
00140 $lines = $wgMemc->get( $key );
00141
00142 $count = $dbr->estimateRowCount( 'page', '*', $where, __METHOD__ );
00143 $maxPerSubpage = intval($count/$this->maxLineCount);
00144 $maxPerSubpage = max($maxPerSubpage,$this->maxPerPage);
00145
00146 if( !is_array( $lines ) ) {
00147 $options = array( 'LIMIT' => 1 );
00148 $options['ORDER BY'] = 'page_title ASC';
00149 $firstTitle = $dbr->selectField( 'page', 'page_title', $where, __METHOD__, $options );
00150 $lastTitle = $firstTitle;
00151 # This array is going to hold the page_titles in order.
00152 $lines = array( $firstTitle );
00153 # If we are going to show n rows, we need n+1 queries to find the relevant titles.
00154 $done = false;
00155 while( !$done ) {
00156
00157 $chunk = ( $lastTitle === false )
00158 ? array()
00159 : array( 'page_title >= ' . $dbr->addQuotes( $lastTitle ) );
00160 $res = $dbr->select( 'page',
00161 'page_title',
00162 array_merge($where,$chunk),
00163 __METHOD__,
00164 array ('LIMIT' => 2, 'OFFSET' => $maxPerSubpage - 1, 'ORDER BY' => 'page_title ASC')
00165 );
00166
00167 if( $s = $dbr->fetchObject( $res ) ) {
00168 array_push( $lines, $s->page_title );
00169 } else {
00170
00171 $endTitle = $dbr->selectField( 'page', 'MAX(page_title)',
00172 array_merge($where,$chunk),
00173 __METHOD__ );
00174 array_push( $lines, $endTitle );
00175 $done = true;
00176 }
00177 if( $s = $res->fetchObject() ) {
00178 array_push( $lines, $s->page_title );
00179 $lastTitle = $s->page_title;
00180 } else {
00181
00182
00183 $done = true;
00184 }
00185 $res->free();
00186 }
00187 $wgMemc->add( $key, $lines, 3600 );
00188 }
00189
00190
00191
00192 if( count( $lines ) <= 2 ) {
00193 if( !empty($lines) ) {
00194 $this->showChunk( $namespace, $from, $to );
00195 } else {
00196 $wgOut->addHTML( $this->namespaceForm( $namespace, $from, $to ) );
00197 }
00198 return;
00199 }
00200
00201 # At this point, $lines should contain an even number of elements.
00202 $out .= Xml::openElement( 'table', array( 'class' => 'allpageslist' ) );
00203 while( count ( $lines ) > 0 ) {
00204 $inpoint = array_shift( $lines );
00205 $outpoint = array_shift( $lines );
00206 $out .= $this->showline( $inpoint, $outpoint, $namespace );
00207 }
00208 $out .= Xml::closeElement( 'table' );
00209 $nsForm = $this->namespaceForm( $namespace, $from, $to );
00210
00211 # Is there more?
00212 if( $this->including() ) {
00213 $out2 = '';
00214 } else {
00215 if( isset($from) || isset($to) ) {
00216 global $wgUser;
00217 $out2 = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-form' ) ).
00218 '<tr>
00219 <td>' .
00220 $nsForm .
00221 '</td>
00222 <td class="mw-allpages-nav">' .
00223 $wgUser->getSkin()->link( $this->getTitle(), wfMsgHtml ( 'allpages' ),
00224 array(), array(), 'known' ) .
00225 "</td>
00226 </tr>" .
00227 Xml::closeElement( 'table' );
00228 } else {
00229 $out2 = $nsForm;
00230 }
00231 }
00232 $wgOut->addHTML( $out2 . $out );
00233 }
00234
00241 function showline( $inpoint, $outpoint, $namespace = NS_MAIN ) {
00242 global $wgContLang;
00243 $inpointf = htmlspecialchars( str_replace( '_', ' ', $inpoint ) );
00244 $outpointf = htmlspecialchars( str_replace( '_', ' ', $outpoint ) );
00245
00246 $inpointf = $wgContLang->truncate( $inpointf, $this->maxPageLength );
00247 $outpointf = $wgContLang->truncate( $outpointf, $this->maxPageLength );
00248
00249 $queryparams = $namespace ? "namespace=$namespace&" : '';
00250 $special = $this->getTitle();
00251 $link = $special->escapeLocalUrl( $queryparams . 'from=' . urlencode($inpoint) . '&to=' . urlencode($outpoint) );
00252
00253 $out = wfMsgHtml( 'alphaindexline',
00254 "<a href=\"$link\">$inpointf</a></td><td>",
00255 "</td><td><a href=\"$link\">$outpointf</a>"
00256 );
00257 return '<tr><td class="mw-allpages-alphaindexline">' . $out . '</td></tr>';
00258 }
00259
00265 function showChunk( $namespace = NS_MAIN, $from = false, $to = false ) {
00266 global $wgOut, $wgUser, $wgContLang, $wgLang;
00267
00268 $sk = $wgUser->getSkin();
00269
00270 $fromList = $this->getNamespaceKeyAndText($namespace, $from);
00271 $toList = $this->getNamespaceKeyAndText( $namespace, $to );
00272 $namespaces = $wgContLang->getNamespaces();
00273 $n = 0;
00274
00275 if ( !$fromList || !$toList ) {
00276 $out = wfMsgWikiHtml( 'allpagesbadtitle' );
00277 } elseif ( !in_array( $namespace, array_keys( $namespaces ) ) ) {
00278
00279 $out = wfMsgExt( 'allpages-bad-ns', array( 'parseinline' ), $namespace );
00280 $namespace = NS_MAIN;
00281 } else {
00282 list( $namespace, $fromKey, $from ) = $fromList;
00283 list( $namespace2, $toKey, $to ) = $toList;
00284
00285 $dbr = wfGetDB( DB_SLAVE );
00286 $conds = array(
00287 'page_namespace' => $namespace,
00288 'page_title >= ' . $dbr->addQuotes( $fromKey )
00289 );
00290 if( $toKey !== "" ) {
00291 $conds[] = 'page_title <= ' . $dbr->addQuotes( $toKey );
00292 }
00293
00294 $res = $dbr->select( 'page',
00295 array( 'page_namespace', 'page_title', 'page_is_redirect' ),
00296 $conds,
00297 __METHOD__,
00298 array(
00299 'ORDER BY' => 'page_title',
00300 'LIMIT' => $this->maxPerPage + 1,
00301 'USE INDEX' => 'name_title',
00302 )
00303 );
00304
00305 if( $res->numRows() > 0 ) {
00306 $out = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-chunk' ) );
00307 while( ( $n < $this->maxPerPage ) && ( $s = $res->fetchObject() ) ) {
00308 $t = Title::makeTitle( $s->page_namespace, $s->page_title );
00309 if( $t ) {
00310 $link = ( $s->page_is_redirect ? '<div class="allpagesredirect">' : '' ) .
00311 $sk->linkKnown( $t, htmlspecialchars( $t->getText() ) ) .
00312 ($s->page_is_redirect ? '</div>' : '' );
00313 } else {
00314 $link = '[[' . htmlspecialchars( $s->page_title ) . ']]';
00315 }
00316 if( $n % 3 == 0 ) {
00317 $out .= '<tr>';
00318 }
00319 $out .= "<td width=\"33%\">$link</td>";
00320 $n++;
00321 if( $n % 3 == 0 ) {
00322 $out .= "</tr>\n";
00323 }
00324 }
00325 if( ($n % 3) != 0 ) {
00326 $out .= "</tr>\n";
00327 }
00328 $out .= Xml::closeElement( 'table' );
00329 } else {
00330 $out = '';
00331 }
00332 }
00333
00334 if ( $this->including() ) {
00335 $out2 = '';
00336 } else {
00337 if( $from == '' ) {
00338
00339 $prevTitle = null;
00340 } else {
00341 # Get the last title from previous chunk
00342 $dbr = wfGetDB( DB_SLAVE );
00343 $res_prev = $dbr->select(
00344 'page',
00345 'page_title',
00346 array( 'page_namespace' => $namespace, 'page_title < '.$dbr->addQuotes($from) ),
00347 __METHOD__,
00348 array( 'ORDER BY' => 'page_title DESC',
00349 'LIMIT' => $this->maxPerPage, 'OFFSET' => ($this->maxPerPage - 1 )
00350 )
00351 );
00352
00353 # Get first title of previous complete chunk
00354 if( $dbr->numrows( $res_prev ) >= $this->maxPerPage ) {
00355 $pt = $dbr->fetchObject( $res_prev );
00356 $prevTitle = Title::makeTitle( $namespace, $pt->page_title );
00357 } else {
00358 # The previous chunk is not complete, need to link to the very first title
00359 # available in the database
00360 $options = array( 'LIMIT' => 1 );
00361 if ( ! $dbr->implicitOrderby() ) {
00362 $options['ORDER BY'] = 'page_title';
00363 }
00364 $reallyFirstPage_title = $dbr->selectField( 'page', 'page_title',
00365 array( 'page_namespace' => $namespace ), __METHOD__, $options );
00366 # Show the previous link if it s not the current requested chunk
00367 if( $from != $reallyFirstPage_title ) {
00368 $prevTitle = Title::makeTitle( $namespace, $reallyFirstPage_title );
00369 } else {
00370 $prevTitle = null;
00371 }
00372 }
00373 }
00374
00375 $self = $this->getTitle();
00376
00377 $nsForm = $this->namespaceForm( $namespace, $from, $to );
00378 $out2 = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-form' ) ).
00379 '<tr>
00380 <td>' .
00381 $nsForm .
00382 '</td>
00383 <td class="mw-allpages-nav">' .
00384 $sk->link( $self, wfMsgHtml ( 'allpages' ), array(), array(), 'known' );
00385
00386 # Do we put a previous link ?
00387 if( isset( $prevTitle ) && $pt = $prevTitle->getText() ) {
00388 $query = array( 'from' => $prevTitle->getText() );
00389
00390 if( $namespace )
00391 $query['namespace'] = $namespace;
00392
00393 $prevLink = $sk->linkKnown(
00394 $self,
00395 htmlspecialchars( wfMsg( 'prevpage', $pt ) ),
00396 array(),
00397 $query
00398 );
00399 $out2 = $wgLang->pipeList( array( $out2, $prevLink ) );
00400 }
00401
00402 if( $n == $this->maxPerPage && $s = $res->fetchObject() ) {
00403 # $s is the first link of the next chunk
00404 $t = Title::MakeTitle($namespace, $s->page_title);
00405 $query = array( 'from' => $t->getText() );
00406
00407 if( $namespace )
00408 $query['namespace'] = $namespace;
00409
00410 $nextLink = $sk->linkKnown(
00411 $self,
00412 htmlspecialchars( wfMsg( 'nextpage', $t->getText() ) ),
00413 array(),
00414 $query
00415 );
00416 $out2 = $wgLang->pipeList( array( $out2, $nextLink ) );
00417 }
00418 $out2 .= "</td></tr></table>";
00419 }
00420
00421 $wgOut->addHTML( $out2 . $out );
00422 if( isset($prevLink) or isset($nextLink) ) {
00423 $wgOut->addHTML( '<hr /><p class="mw-allpages-nav">' );
00424 if( isset( $prevLink ) ) {
00425 $wgOut->addHTML( $prevLink );
00426 }
00427 if( isset( $prevLink ) && isset( $nextLink ) ) {
00428 $wgOut->addHTML( wfMsgExt( 'pipe-separator' , 'escapenoentities' ) );
00429 }
00430 if( isset( $nextLink ) ) {
00431 $wgOut->addHTML( $nextLink );
00432 }
00433 $wgOut->addHTML( '</p>' );
00434
00435 }
00436
00437 }
00438
00446 function getNamespaceKeyAndText($ns, $text) {
00447 if ( $text == '' )
00448 return array( $ns, '', '' ); # shortcut for common case
00449
00450 $t = Title::makeTitleSafe($ns, $text);
00451 if ( $t && $t->isLocal() ) {
00452 return array( $t->getNamespace(), $t->getDBkey(), $t->getText() );
00453 } else if ( $t ) {
00454 return null;
00455 }
00456
00457 # try again, in case the problem was an empty pagename
00458 $text = preg_replace('/(#|$)/', 'X$1', $text);
00459 $t = Title::makeTitleSafe($ns, $text);
00460 if ( $t && $t->isLocal() ) {
00461 return array( $t->getNamespace(), '', '' );
00462 } else {
00463 return null;
00464 }
00465 }
00466 }