MediaWiki  REL1_20
SpecialAllpages.php
Go to the documentation of this file.
00001 <?php
00029 class SpecialAllpages extends IncludableSpecialPage {
00030 
00036         protected $maxPerPage = 345;
00037 
00043         protected $maxLineCount = 100;
00044 
00050         protected $maxPageLength = 70;
00051 
00057         protected $nsfromMsg = 'allpagesfrom';
00058 
00064         function __construct( $name = 'Allpages' ){
00065                 parent::__construct( $name );
00066         }
00067 
00073         function execute( $par ) {
00074                 global $wgContLang;
00075                 $request = $this->getRequest();
00076                 $out = $this->getOutput();
00077 
00078                 $this->setHeaders();
00079                 $this->outputHeader();
00080                 $out->allowClickjacking();
00081 
00082                 # GET values
00083                 $from = $request->getVal( 'from', null );
00084                 $to = $request->getVal( 'to', null );
00085                 $namespace = $request->getInt( 'namespace' );
00086                 $hideredirects = $request->getBool( 'hideredirects', false );
00087 
00088                 $namespaces = $wgContLang->getNamespaces();
00089 
00090                 $out->setPageTitle(
00091                         ( $namespace > 0 && in_array( $namespace, array_keys( $namespaces) ) ) ?
00092                         $this->msg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) ) :
00093                         $this->msg( 'allarticles' )
00094                 );
00095                 $out->addModuleStyles( 'mediawiki.special' );
00096 
00097                 if( $par !== null ) {
00098                         $this->showChunk( $namespace, $par, $to, $hideredirects );
00099                 } elseif( $from !== null && $to === null ) {
00100                         $this->showChunk( $namespace, $from, $to, $hideredirects );
00101                 } else {
00102                         $this->showToplevel( $namespace, $from, $to, $hideredirects );
00103                 }
00104         }
00105 
00115         function namespaceForm( $namespace = NS_MAIN, $from = '', $to = '', $hideredirects = false ) {
00116                 global $wgScript;
00117                 $t = $this->getTitle();
00118 
00119                 $out  = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) );
00120                 $out .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
00121                 $out .= Html::hidden( 'title', $t->getPrefixedText() );
00122                 $out .= Xml::openElement( 'fieldset' );
00123                 $out .= Xml::element( 'legend', null, $this->msg( 'allpages' )->text() );
00124                 $out .= Xml::openElement( 'table', array( 'id' => 'nsselect', 'class' => 'allpages' ) );
00125                 $out .= "<tr>
00126         <td class='mw-label'>" .
00127                         Xml::label( $this->msg( 'allpagesfrom' )->text(), 'nsfrom' ) .
00128                         "       </td>
00129         <td class='mw-input'>" .
00130                         Xml::input( 'from', 30, str_replace('_',' ',$from), array( 'id' => 'nsfrom' ) ) .
00131                         "       </td>
00132 </tr>
00133 <tr>
00134         <td class='mw-label'>" .
00135                         Xml::label( $this->msg( 'allpagesto' )->text(), 'nsto' ) .
00136                         "       </td>
00137                         <td class='mw-input'>" .
00138                         Xml::input( 'to', 30, str_replace('_',' ',$to), array( 'id' => 'nsto' ) ) .
00139                         "               </td>
00140 </tr>
00141 <tr>
00142         <td class='mw-label'>" .
00143                         Xml::label( $this->msg( 'namespace' )->text(), 'namespace' ) .
00144                         "       </td>
00145                         <td class='mw-input'>" .
00146                         Html::namespaceSelector(
00147                                 array( 'selected' => $namespace ),
00148                                 array( 'name' => 'namespace', 'id' => 'namespace' )
00149                         ) . ' ' .
00150                         Xml::checkLabel(
00151                                 $this->msg( 'allpages-hide-redirects' )->text(),
00152                                 'hideredirects',
00153                                 'hideredirects',
00154                                 $hideredirects
00155                         ) . ' ' .
00156                         Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ) .
00157                         "       </td>
00158 </tr>";
00159                 $out .= Xml::closeElement( 'table' );
00160                 $out .= Xml::closeElement( 'fieldset' );
00161                 $out .= Xml::closeElement( 'form' );
00162                 $out .= Xml::closeElement( 'div' );
00163                 return $out;
00164         }
00165 
00172         function showToplevel( $namespace = NS_MAIN, $from = '', $to = '', $hideredirects = false ) {
00173                 $output = $this->getOutput();
00174 
00175                 # TODO: Either make this *much* faster or cache the title index points
00176                 # in the querycache table.
00177 
00178                 $dbr = wfGetDB( DB_SLAVE );
00179                 $out = "";
00180                 $where = array( 'page_namespace' => $namespace );
00181 
00182                 if ( $hideredirects ) {
00183                         $where[ 'page_is_redirect' ] = 0;
00184                 }
00185 
00186                 $from = Title::makeTitleSafe( $namespace, $from );
00187                 $to = Title::makeTitleSafe( $namespace, $to );
00188                 $from = ( $from && $from->isLocal() ) ? $from->getDBkey() : null;
00189                 $to = ( $to && $to->isLocal() ) ? $to->getDBkey() : null;
00190 
00191                 if( isset($from) )
00192                         $where[] = 'page_title >= '.$dbr->addQuotes( $from );
00193                 if( isset($to) )
00194                         $where[] = 'page_title <= '.$dbr->addQuotes( $to );
00195 
00196                 global $wgMemc;
00197                 $key = wfMemcKey( 'allpages', 'ns', $namespace, $from, $to );
00198                 $lines = $wgMemc->get( $key );
00199 
00200                 $count = $dbr->estimateRowCount( 'page', '*', $where, __METHOD__ );
00201                 $maxPerSubpage = intval($count/$this->maxLineCount);
00202                 $maxPerSubpage = max($maxPerSubpage,$this->maxPerPage);
00203 
00204                 if( !is_array( $lines ) ) {
00205                         $options = array( 'LIMIT' => 1 );
00206                         $options['ORDER BY'] = 'page_title ASC';
00207                         $firstTitle = $dbr->selectField( 'page', 'page_title', $where, __METHOD__, $options );
00208                         $lastTitle = $firstTitle;
00209                         # This array is going to hold the page_titles in order.
00210                         $lines = array( $firstTitle );
00211                         # If we are going to show n rows, we need n+1 queries to find the relevant titles.
00212                         $done = false;
00213                         while( !$done ) {
00214                                 // Fetch the last title of this chunk and the first of the next
00215                                 $chunk = ( $lastTitle === false )
00216                                         ? array()
00217                                         : array( 'page_title >= ' . $dbr->addQuotes( $lastTitle ) );
00218                                 $res = $dbr->select( 'page', /* FROM */
00219                                         'page_title', /* WHAT */
00220                                         array_merge($where,$chunk),
00221                                         __METHOD__,
00222                                         array ('LIMIT' => 2, 'OFFSET' => $maxPerSubpage - 1, 'ORDER BY' => 'page_title ASC')
00223                                 );
00224 
00225                                 $s = $dbr->fetchObject( $res );
00226                                 if( $s ) {
00227                                         array_push( $lines, $s->page_title );
00228                                 } else {
00229                                         // Final chunk, but ended prematurely. Go back and find the end.
00230                                         $endTitle = $dbr->selectField( 'page', 'MAX(page_title)',
00231                                                 array_merge($where,$chunk),
00232                                                 __METHOD__ );
00233                                         array_push( $lines, $endTitle );
00234                                         $done = true;
00235                                 }
00236                                 $s = $res->fetchObject();
00237                                 if( $s ) {
00238                                         array_push( $lines, $s->page_title );
00239                                         $lastTitle = $s->page_title;
00240                                 } else {
00241                                         // This was a final chunk and ended exactly at the limit.
00242                                         // Rare but convenient!
00243                                         $done = true;
00244                                 }
00245                                 $res->free();
00246                         }
00247                         $wgMemc->add( $key, $lines, 3600 );
00248                 }
00249 
00250                 // If there are only two or less sections, don't even display them.
00251                 // Instead, display the first section directly.
00252                 if( count( $lines ) <= 2 ) {
00253                         if( !empty($lines) ) {
00254                                 $this->showChunk( $namespace, $from, $to, $hideredirects );
00255                         } else {
00256                                 $output->addHTML( $this->namespaceForm( $namespace, $from, $to, $hideredirects ) );
00257                         }
00258                         return;
00259                 }
00260 
00261                 # At this point, $lines should contain an even number of elements.
00262                 $out .= Xml::openElement( 'table', array( 'class' => 'allpageslist' ) );
00263                 while( count ( $lines ) > 0 ) {
00264                         $inpoint = array_shift( $lines );
00265                         $outpoint = array_shift( $lines );
00266                         $out .= $this->showline( $inpoint, $outpoint, $namespace, $hideredirects );
00267                 }
00268                 $out .= Xml::closeElement( 'table' );
00269                 $nsForm = $this->namespaceForm( $namespace, $from, $to, $hideredirects );
00270 
00271                 # Is there more?
00272                 if( $this->including() ) {
00273                         $out2 = '';
00274                 } else {
00275                         if( isset($from) || isset($to) ) {
00276                                 $out2 = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-form' ) ).
00277                                                 '<tr>
00278                                                         <td>' .
00279                                                                 $nsForm .
00280                                                         '</td>
00281                                                         <td class="mw-allpages-nav">' .
00282                                                                 Linker::link( $this->getTitle(), $this->msg( 'allpages' )->escaped(),
00283                                                                         array(), array(), 'known' ) .
00284                                                         "</td>
00285                                                 </tr>" .
00286                                         Xml::closeElement( 'table' );
00287                         } else {
00288                                 $out2 = $nsForm;
00289                         }
00290                 }
00291                 $output->addHTML( $out2 . $out );
00292         }
00293 
00303         function showline( $inpoint, $outpoint, $namespace = NS_MAIN, $hideredirects ) {
00304                 global $wgContLang;
00305                 $inpointf = htmlspecialchars( str_replace( '_', ' ', $inpoint ) );
00306                 $outpointf = htmlspecialchars( str_replace( '_', ' ', $outpoint ) );
00307                 // Don't let the length runaway
00308                 $inpointf = $wgContLang->truncate( $inpointf, $this->maxPageLength );
00309                 $outpointf = $wgContLang->truncate( $outpointf, $this->maxPageLength );
00310 
00311                 $queryparams = $namespace ? "namespace=$namespace&" : '';
00312 
00313                 $queryhideredirects = array();
00314                 if ($hideredirects) {
00315                         $queryhideredirects[ 'hideredirects' ] = 1;
00316                 }
00317 
00318                 $special = $this->getTitle();
00319                 $link = htmlspecialchars( $special->getLocalUrl( $queryparams . 'from=' . urlencode($inpoint) . '&to=' . urlencode($outpoint), $queryhideredirects ) );
00320 
00321                 $out = $this->msg( 'alphaindexline' )->rawParams(
00322                         "<a href=\"$link\">$inpointf</a></td><td>",
00323                         "</td><td><a href=\"$link\">$outpointf</a>"
00324                 )->escaped();
00325                 return '<tr><td class="mw-allpages-alphaindexline">' . $out . '</td></tr>';
00326         }
00327 
00334         function showChunk( $namespace = NS_MAIN, $from = false, $to = false, $hideredirects = false ) {
00335                 global $wgContLang;
00336                 $output = $this->getOutput();
00337 
00338                 $fromList = $this->getNamespaceKeyAndText($namespace, $from);
00339                 $toList = $this->getNamespaceKeyAndText( $namespace, $to );
00340                 $namespaces = $wgContLang->getNamespaces();
00341                 $n = 0;
00342 
00343                 if ( !$fromList || !$toList ) {
00344                         $out = $this->msg( 'allpagesbadtitle' )->parseAsBlock();
00345                 } elseif ( !in_array( $namespace, array_keys( $namespaces ) ) ) {
00346                         // Show errormessage and reset to NS_MAIN
00347                         $out = $this->msg( 'allpages-bad-ns', $namespace )->parse();
00348                         $namespace = NS_MAIN;
00349                 } else {
00350                         list( $namespace, $fromKey, $from ) = $fromList;
00351                         list( , $toKey, $to ) = $toList;
00352 
00353                         $dbr = wfGetDB( DB_SLAVE );
00354                         $conds = array(
00355                                 'page_namespace' => $namespace,
00356                                 'page_title >= ' . $dbr->addQuotes( $fromKey )
00357                         );
00358 
00359                         if ( $hideredirects ) {
00360                                 $conds[ 'page_is_redirect' ] = 0;
00361                         }
00362 
00363                         if( $toKey !== "" ) {
00364                                 $conds[] = 'page_title <= ' . $dbr->addQuotes( $toKey );
00365                         }
00366 
00367                         $res = $dbr->select( 'page',
00368                                 array( 'page_namespace', 'page_title', 'page_is_redirect', 'page_id' ),
00369                                 $conds,
00370                                 __METHOD__,
00371                                 array(
00372                                         'ORDER BY'  => 'page_title',
00373                                         'LIMIT'     => $this->maxPerPage + 1,
00374                                         'USE INDEX' => 'name_title',
00375                                 )
00376                         );
00377 
00378                         if( $res->numRows() > 0 ) {
00379                                 $out = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-chunk' ) );
00380                                 while( ( $n < $this->maxPerPage ) && ( $s = $res->fetchObject() ) ) {
00381                                         $t = Title::newFromRow( $s );
00382                                         if( $t ) {
00383                                                 $link = ( $s->page_is_redirect ? '<div class="allpagesredirect">' : '' ) .
00384                                                         Linker::link( $t ) .
00385                                                         ($s->page_is_redirect ? '</div>' : '' );
00386                                         } else {
00387                                                 $link = '[[' . htmlspecialchars( $s->page_title ) . ']]';
00388                                         }
00389                                         if( $n % 3 == 0 ) {
00390                                                 $out .= '<tr>';
00391                                         }
00392                                         $out .= "<td style=\"width:33%\">$link</td>";
00393                                         $n++;
00394                                         if( $n % 3 == 0 ) {
00395                                                 $out .= "</tr>\n";
00396                                         }
00397                                 }
00398                                 if( ($n % 3) != 0 ) {
00399                                         $out .= "</tr>\n";
00400                                 }
00401                                 $out .= Xml::closeElement( 'table' );
00402                         } else {
00403                                 $out = '';
00404                         }
00405                 }
00406 
00407                 if ( $this->including() ) {
00408                         $out2 = '';
00409                 } else {
00410                         if( $from == '' ) {
00411                                 // First chunk; no previous link.
00412                                 $prevTitle = null;
00413                         } else {
00414                                 # Get the last title from previous chunk
00415                                 $dbr = wfGetDB( DB_SLAVE );
00416                                 $res_prev = $dbr->select(
00417                                         'page',
00418                                         'page_title',
00419                                         array( 'page_namespace' => $namespace, 'page_title < '.$dbr->addQuotes($from) ),
00420                                         __METHOD__,
00421                                         array( 'ORDER BY' => 'page_title DESC',
00422                                                 'LIMIT' => $this->maxPerPage, 'OFFSET' => ($this->maxPerPage - 1 )
00423                                         )
00424                                 );
00425 
00426                                 # Get first title of previous complete chunk
00427                                 if( $dbr->numrows( $res_prev ) >= $this->maxPerPage ) {
00428                                         $pt = $dbr->fetchObject( $res_prev );
00429                                         $prevTitle = Title::makeTitle( $namespace, $pt->page_title );
00430                                 } else {
00431                                         # The previous chunk is not complete, need to link to the very first title
00432                                         # available in the database
00433                                         $options = array( 'LIMIT' => 1 );
00434                                         if ( ! $dbr->implicitOrderby() ) {
00435                                                 $options['ORDER BY'] = 'page_title';
00436                                         }
00437                                         $reallyFirstPage_title = $dbr->selectField( 'page', 'page_title',
00438                                                 array( 'page_namespace' => $namespace ), __METHOD__, $options );
00439                                         # Show the previous link if it s not the current requested chunk
00440                                         if( $from != $reallyFirstPage_title ) {
00441                                                 $prevTitle =  Title::makeTitle( $namespace, $reallyFirstPage_title );
00442                                         } else {
00443                                                 $prevTitle = null;
00444                                         }
00445                                 }
00446                         }
00447 
00448                         $self = $this->getTitle();
00449 
00450                         $nsForm = $this->namespaceForm( $namespace, $from, $to, $hideredirects );
00451                         $out2 = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-form' ) ).
00452                                                 '<tr>
00453                                                         <td>' .
00454                                                                 $nsForm .
00455                                                         '</td>
00456                                                         <td class="mw-allpages-nav">' .
00457                                                                 Linker::link( $self, $this->msg( 'allpages' )->escaped() );
00458 
00459                         # Do we put a previous link ?
00460                         if( isset( $prevTitle ) &&  $pt = $prevTitle->getText() ) {
00461                                 $query = array( 'from' => $prevTitle->getText() );
00462 
00463                                 if( $namespace )
00464                                         $query['namespace'] = $namespace;
00465 
00466                                 if( $hideredirects )
00467                                         $query['hideredirects'] = $hideredirects;
00468 
00469                                 $prevLink = Linker::linkKnown(
00470                                         $self,
00471                                         $this->msg( 'prevpage', $pt )->escaped(),
00472                                         array(),
00473                                         $query
00474                                 );
00475                                 $out2 = $this->getLanguage()->pipeList( array( $out2, $prevLink ) );
00476                         }
00477 
00478                         if( $n == $this->maxPerPage && $s = $res->fetchObject() ) {
00479                                 # $s is the first link of the next chunk
00480                                 $t = Title::makeTitle($namespace, $s->page_title);
00481                                 $query = array( 'from' => $t->getText() );
00482 
00483                                 if( $namespace )
00484                                         $query['namespace'] = $namespace;
00485 
00486                                 if( $hideredirects )
00487                                         $query['hideredirects'] = $hideredirects;
00488 
00489                                 $nextLink = Linker::linkKnown(
00490                                         $self,
00491                                         $this->msg( 'nextpage', $t->getText() )->escaped(),
00492                                         array(),
00493                                         $query
00494                                 );
00495                                 $out2 = $this->getLanguage()->pipeList( array( $out2, $nextLink ) );
00496                         }
00497                         $out2 .= "</td></tr></table>";
00498                 }
00499 
00500                 $output->addHTML( $out2 . $out );
00501 
00502                 $links = array();
00503                 if ( isset( $prevLink ) ) $links[] = $prevLink;
00504                 if ( isset( $nextLink ) ) $links[] = $nextLink;
00505 
00506                 if ( count( $links ) ) {
00507                         $output->addHTML(
00508                                 Html::element( 'hr' ) .
00509                                 Html::rawElement( 'div', array( 'class' => 'mw-allpages-nav' ),
00510                                         $this->getLanguage()->pipeList( $links )
00511                                 ) );
00512                 }
00513 
00514         }
00515 
00521         protected function getNamespaceKeyAndText($ns, $text) {
00522                 if ( $text == '' )
00523                         return array( $ns, '', '' ); # shortcut for common case
00524 
00525                 $t = Title::makeTitleSafe($ns, $text);
00526                 if ( $t && $t->isLocal() ) {
00527                         return array( $t->getNamespace(), $t->getDBkey(), $t->getText() );
00528                 } elseif ( $t ) {
00529                         return null;
00530                 }
00531 
00532                 # try again, in case the problem was an empty pagename
00533                 $text = preg_replace('/(#|$)/', 'X$1', $text);
00534                 $t = Title::makeTitleSafe($ns, $text);
00535                 if ( $t && $t->isLocal() ) {
00536                         return array( $t->getNamespace(), '', '' );
00537                 } else {
00538                         return null;
00539                 }
00540         }
00541 }