MediaWiki
REL1_20
|
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 }