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